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>
This commit is contained in:
Bonchellon
2026-07-09 13:50:45 +03:00
parent 4e4e0f215a
commit f0fc2f687a
14 changed files with 1127 additions and 37 deletions

View File

@ -108,6 +108,14 @@ export const p4 = {
scanCancel: () => invoke<void>("p4_scan_cancel"),
// ask an in-progress submit/sync transfer to stop (safe pre-commit for submit)
transferCancel: () => invoke<void>("p4_transfer_cancel"),
// --- plugins ---
pluginList: () => invoke<PluginInfo[]>("plugin_list"),
pluginReadEntry: (id: string) => invoke<string>("plugin_read_entry", { id }),
pluginSetEnabled: (id: string, enabled: boolean) => invoke<void>("plugin_set_enabled", { id, enabled }),
pluginInstall: (src: string) => invoke<string>("plugin_install", { src }),
pluginRemove: (id: string) => invoke<void>("plugin_remove", { id }),
pluginsDirPath: () => invoke<string>("plugins_dir_path"),
openPluginsDir: () => invoke<void>("open_plugins_dir"),
describe: (change: string) => invoke<Describe>("p4_describe", { change }),
undoChange: (change: string) => invoke<string>("p4_undo_change", { change }),
openInExplorer: (target: string) => invoke<void>("open_in_explorer", { target }),
@ -196,6 +204,7 @@ export function getEditor(): string { try { return localStorage.getItem("exd-edi
export function setEditorPref(id: string) { try { localStorage.setItem("exd-editor", id); } catch {} }
export interface Dir { dir?: string; local?: boolean; [k: string]: unknown }
export interface PluginInfo { id: string; name: string; version: string; description: string; author: string; permissions: string[]; manifest: Record<string, unknown>; enabled: boolean; dir: string }
// depot path -> short leaf name (last segment)
export function leaf(path?: string): string {
if (!path) return "";