From 1e9ffb6109ba4abe17a1ce3cf3905e81139c390d Mon Sep 17 00:00:00 2001 From: Bonchellon Date: Thu, 9 Jul 2026 14:51:52 +0300 Subject: [PATCH] Initial plugin registry: unreal + hello-exbyte --- README.md | 9 +++++ plugins/hello-exbyte/index.js | 57 ++++++++++++++++++++++++++++++++ plugins/hello-exbyte/plugin.json | 11 ++++++ plugins/unreal/index.js | 46 ++++++++++++++++++++++++++ plugins/unreal/plugin.json | 12 +++++++ registry.json | 32 ++++++++++++++++++ 6 files changed, 167 insertions(+) create mode 100644 README.md create mode 100644 plugins/hello-exbyte/index.js create mode 100644 plugins/hello-exbyte/plugin.json create mode 100644 plugins/unreal/index.js create mode 100644 plugins/unreal/plugin.json create mode 100644 registry.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..a9428c7 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Exbyte Depot β€” official plugin registry + +`registry.json` lists official plugins for **Exbyte Depot**. The client fetches it, +shows available plugins, and installs them into the user's local plugins folder. +Plugins marked `"autoInstall": true` are installed automatically on first run +(e.g. **Unreal Engine Tools**). + +Each plugin lives under `plugins//` (a `plugin.json` manifest + a JS entry). +See the app's `PLUGINS.md` for the SDK. diff --git a/plugins/hello-exbyte/index.js b/plugins/hello-exbyte/index.js new file mode 100644 index 0000000..b57b8c4 --- /dev/null +++ b/plugins/hello-exbyte/index.js @@ -0,0 +1,57 @@ +// Hello Exbyte β€” example plugin. +// A plugin is a plain ES module that exports `activate(host)`. The host is the +// only way the plugin touches the app; what it can reach is gated by the +// "permissions" in plugin.json. + +export function activate(host) { + host.log("activating", host.plugin.id, "v", host.version); + + // 1) A menu action (appears under the Actions menu). + host.ui.addMenuItem("Say hello πŸ‘‹", () => { + const me = host.info()?.userName || "there"; + host.flash(`Hello, ${me}! (from the Hello Exbyte plugin)`); + host.notify("Hello Exbyte", "The example plugin says hi."); + }); + + // 2) A file context-menu item (right-click a file in Changes). + host.ui.addFileContextItem("Copy depot path (plugin)", (file) => { + if (file.depotFile) { + host.clipboard.write(file.depotFile); + host.flash("Copied: " + file.depotFile); + } + }); + + // 3) A view β€” a per-workspace notepad, persisted with host.storage. + // Opens from the Actions menu (listed by its title). + host.ui.addView({ + id: "notes", + title: "Workspace Notes", + render: (el) => { + const key = "notes:" + (host.info()?.clientName || "default"); + const ta = document.createElement("textarea"); + ta.value = host.storage.get(key) || ""; + ta.placeholder = "Jot notes for this workspace… (saved locally, per workspace)"; + ta.style.cssText = + "width:100%;height:320px;box-sizing:border-box;resize:vertical;" + + "background:var(--input-bg,#111);color:var(--txt,#eee);" + + "border:1px solid var(--border,#333);border-radius:10px;padding:12px;" + + "font:13px/1.6 system-ui;outline:none"; + ta.addEventListener("input", () => host.storage.set(key, ta.value)); + el.appendChild(ta); + // optional cleanup returned to the host: + return () => host.storage.set(key, ta.value); + }, + }); + + // 4) A submit guard β€” blocks (with a confirm) files that look like WIP. + // This is the same hook point a reference-integrity guard would use. + host.ui.addSubmitHook((files) => { + const wip = files.filter((f) => /(^|\/)(wip|temp|scratch)[^/]*$/i.test(f.depotFile || "")); + if (wip.length) { + return { ok: false, message: `${wip.length} file(s) look like work-in-progress (wip / temp / scratch).` }; + } + return { ok: true }; + }); + + host.log("activated"); +} diff --git a/plugins/hello-exbyte/plugin.json b/plugins/hello-exbyte/plugin.json new file mode 100644 index 0000000..e300b70 --- /dev/null +++ b/plugins/hello-exbyte/plugin.json @@ -0,0 +1,11 @@ +{ + "id": "hello-exbyte", + "name": "Hello Exbyte", + "version": "1.0.0", + "author": "Exbyte Studios", + "description": "Example plugin β€” a menu action, a per-workspace notes panel, a file context item, and a submit guard. Copy it as a starting point.", + "entry": "index.js", + "minAppVersion": "0.3.5", + "permissions": ["p4:read", "notify"], + "contributes": { "menu": true, "views": ["notes"], "fileContext": true, "submitHooks": true } +} diff --git a/plugins/unreal/index.js b/plugins/unreal/index.js new file mode 100644 index 0000000..db9f4fc --- /dev/null +++ b/plugins/unreal/index.js @@ -0,0 +1,46 @@ +// Unreal Engine Tools β€” official plugin. +// The core app keeps the build engine (UnrealBuildTool / MSBuild), the build +// overlay and the log reader; this plugin is just the Unreal-facing surface: +// a "Build Solution" action and a live Unreal-log view. Reached through the +// permission-gated `host.ue` bridge. + +export function activate(host) { + const ue = host.ue; + if (!ue) { host.log("no ue bridge β€” plugin.json needs the \"ue\" permission"); return; } + + // Build the game target (UnrealBuildTool for the game module, MSBuild for plain C++). + host.ui.addMenuItem("πŸ”¨ Build Solution (Unreal)", () => { + if (!ue.hasProject()) { host.flash("No Unreal project in the current working folder.", true); return; } + ue.build(); // triggers the core build flow + overlay + }); + + // Live Unreal editor log (tails Saved/Logs). Opens from the Actions menu. + host.ui.addView({ + id: "unreal-log", + title: "Unreal Log", + render: (el) => { + el.style.cssText = "display:flex;flex-direction:column;height:100%;min-height:340px"; + const pre = document.createElement("pre"); + pre.style.cssText = + "flex:1;margin:0;overflow:auto;white-space:pre-wrap;word-break:break-word;" + + "font:11.5px/1.6 var(--mono,ui-monospace,monospace);color:var(--muted,#9a9aa8)"; + el.appendChild(pre); + let alive = true; + async function tick() { + if (!alive) return; + try { + if (!ue.hasProject()) { pre.textContent = "No Unreal project in the current working folder."; return; } + const atBottom = pre.scrollTop + pre.clientHeight >= pre.scrollHeight - 30; + const txt = await ue.readLog(200000); + pre.textContent = txt || "No Unreal log yet. It appears when the editor writes to Saved/Logs (i.e. while Unreal is running)."; + if (atBottom) pre.scrollTop = pre.scrollHeight; + } catch (e) { pre.textContent = String(e); } + } + tick(); + const id = setInterval(tick, 1500); + return () => { alive = false; clearInterval(id); }; + }, + }); + + host.log("Unreal Engine Tools activated"); +} diff --git a/plugins/unreal/plugin.json b/plugins/unreal/plugin.json new file mode 100644 index 0000000..dc19d51 --- /dev/null +++ b/plugins/unreal/plugin.json @@ -0,0 +1,12 @@ +{ + "id": "unreal", + "name": "Unreal Engine Tools", + "version": "1.0.0", + "author": "Exbyte Studios", + "description": "Build the game (.sln / UnrealBuildTool) and tail the Unreal editor log. Enable this on Unreal Engine projects.", + "entry": "index.js", + "minAppVersion": "0.3.6", + "permissions": ["ue"], + "defaultEnabled": true, + "contributes": { "menu": true, "views": ["unreal-log"] } +} diff --git a/registry.json b/registry.json new file mode 100644 index 0000000..2800895 --- /dev/null +++ b/registry.json @@ -0,0 +1,32 @@ +{ + "schema": "exbyte-depot-plugin-registry/1", + "updated": "2026-07-09", + "plugins": [ + { + "id": "unreal", + "name": "Unreal Engine Tools", + "version": "1.0.0", + "author": "Exbyte Studios", + "description": "Build the game (.sln / UnrealBuildTool) and tail the Unreal editor log. For Unreal Engine projects.", + "permissions": ["ue"], + "minAppVersion": "0.3.6", + "official": true, + "autoInstall": true, + "files": ["plugin.json", "index.js"], + "baseUrl": "https://gitea.exbytestudios.com/ExbytePublicServices/exbyte-depot-plugins/raw/branch/main/plugins/unreal/" + }, + { + "id": "hello-exbyte", + "name": "Hello Exbyte", + "version": "1.0.0", + "author": "Exbyte Studios", + "description": "Example plugin β€” menu action, per-workspace notes, file context item, submit guard. A starting point for your own.", + "permissions": ["p4:read", "notify"], + "minAppVersion": "0.3.5", + "official": true, + "autoInstall": false, + "files": ["plugin.json", "index.js"], + "baseUrl": "https://gitea.exbytestudios.com/ExbytePublicServices/exbyte-depot-plugins/raw/branch/main/plugins/hello-exbyte/" + } + ] +}