Files
Bonchellon f0fc2f687a v0.3.5 — theme system, plugin platform, sectioned Settings, tab animations
- Themes: built-in presets (Ember/Forest/Ocean/Rosé) + custom-theme editor
  (per-token color pickers, live preview, JSON export/import). src/themes.ts.
- Plugin platform: JS/ESM plugins loaded via blob-module import (CSP
  script-src blob:), Host SDK with menu / file-context / view / submit-hook
  contribution points + permission model. Backend commands to list/read/enable/
  install/remove plugins under <config>/plugins/. Example plugin + PLUGINS.md.
- Settings redesigned into a sectioned hub (General / Appearance / Plugins);
  Themes and Plugins moved in, removed from the Actions menu.
- "NEW" badge on History commits submitted < 3h ago.
- Smooth tab transitions: sliding underline + content fade on Changes/History,
  and matching fades on the view-panel and dock tab groups.
- Fix: settings close button used the default browser style.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 13:50:45 +03:00

58 lines
2.3 KiB
JavaScript

// 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");
}