diff --git a/packages/plugin-sdk/CHANGELOG.md b/packages/plugin-sdk/CHANGELOG.md index 01bcd3746..6ca580fc2 100644 --- a/packages/plugin-sdk/CHANGELOG.md +++ b/packages/plugin-sdk/CHANGELOG.md @@ -8,7 +8,7 @@ Plugin API 1.4.0. - `contributes.layouts` and `app.registerLayout()` for a whole-app layout, with the `ui:layout` capability. Needs `engine.api` 1.4 - `contributes.themes` for CSS-only themes, with the `ui:theme` capability. `termix-plugin validate` checks theme CSS and `pack` ships it -- `useShell()` gives a layout the live shell: tabs, splits, hosts, docks, the command palette and the registries +- `useShell()` gives a layout the live shell: tabs, splits, hosts, docks, the command palette and the registries. The shell's home tab is left out until some plugin registers a `dashboard` tab type - `TabStage` and `ShellConfirmCapture` in `@termix-ssh/plugin-sdk/ui`, for a layout to place tabs - `validateThemeCss()` in `@termix-ssh/plugin-sdk/manifest` - `@termix-ssh/plugin-sdk/ui` also exports what a whole layout is built from: the API client, shared types, hooks, `useShellController()` and `APP_VERSION`. These reach into core, so they can change with a core release @@ -19,6 +19,7 @@ Plugin API 1.4.0. ### Fixed - The manifest JSON schema lists the `notify:hub` and `plugins:read` capabilities +- Plugin tests and type checks work with the SDK linked from a local Termix checkout ## 1.0.11 diff --git a/packages/plugin-sdk/PLUGIN-GUIDE.md b/packages/plugin-sdk/PLUGIN-GUIDE.md index c1120546d..e085c0aa1 100644 --- a/packages/plugin-sdk/PLUGIN-GUIDE.md +++ b/packages/plugin-sdk/PLUGIN-GUIDE.md @@ -543,7 +543,15 @@ export function activate(app: TermixApp) { command palette, the registries as hooks and the user. Core keeps all the state, so switching layouts keeps every session. - `TabStage` is where tabs show. Place it anywhere; tabs are never remounted when it moves, so a terminal - keeps its connection. The command palette, overlays and onboarding stay in core. + keeps its connection. Wrap it in `SurfaceScope kind="tab"` with a `ShellConfirmCapture` inside, so + close questions and inline views show over the tab area. Overlays and onboarding stay in core. +- The layout draws the command palette. Core keeps whether it is open (`palette.isOpen`, Ctrl+K calls + `palette.open()`) and what is in it (`usePaletteEntries`, `usePaletteGroups`, rail items, hosts). +- The shell always keeps a home tab with the id and type `dashboard`. `useShell()` leaves it out until some + plugin registers a `dashboard` tab type, so a layout without a home screen has no empty tab to draw. +- Settings, Manage and the host editor belong to Classic Layout. A layout without them should still render + `LookSettings` somewhere so people can switch back. `tabs.openSingleton("plugins")` opens core's own + Plugins tab in any layout. Switching layouts closes the tabs the old layout's plugin declared. - `mobileComponent` is used on phones and narrow windows. The desktop app always gets `component`. - Only the chosen layout's plugin is loaded. If it fails to load or crashes twice, Termix shows a repair screen where people can switch, reset, update, turn it off or (for a bundled plugin) restore the copy that @@ -555,6 +563,8 @@ export function activate(app: TermixApp) { `classic-layout.hostTree` and `classic-layout.dashboard` for other plugins to place with `PluginComponent`. It reads the shell with `useShellController()` from `@termix-ssh/plugin-sdk/ui`, which exposes more than `useShell()` and may change with Termix. A third party layout should start from `useShell()`. +- Example Top Bar Layout (https://github.com/Termix-SSH/Plugin-Example-Topbar-Layout) is a small third party + style layout built only on `useShell()`, `TabStage` and `@termix-ssh/plugin-sdk/ui`, with a phone version. A theme is CSS only. Declare it in `contributes.themes` (`id`, `label`, `mode` light or dark, `css` path, optional `swatch` colors for the picker) with the `ui:theme` capability. The id becomes a class on ``, @@ -572,7 +582,9 @@ A plugin that only holds themes needs no code. Leave out `src/`: `termix-plugin - Themes show in the picker in the order core lists its own, then the plugin's order. - A theme someone saved that no longer exists shows as System. It comes back when the plugin does. - Accent color, UI font, font size and density are not part of a theme. Core applies them on top. -- Bundled Classic Themes (https://github.com/Termix-SSH/Plugin-Classic-Themes) is a full example. +- Bundled Classic Themes (https://github.com/Termix-SSH/Plugin-Classic-Themes) is a full example, and + Example Theme (https://github.com/Termix-SSH/Plugin-Example-Theme) a small one with one light and one dark + theme. ## Looking native diff --git a/packages/plugin-sdk/src/vitest-preset.ts b/packages/plugin-sdk/src/vitest-preset.ts index 6bfd03b43..95f281fe9 100644 --- a/packages/plugin-sdk/src/vitest-preset.ts +++ b/packages/plugin-sdk/src/vitest-preset.ts @@ -117,9 +117,19 @@ export function pluginVitestConfig( const plugins = [wasmBinaryPlugin()]; + // A local SDK linked into node_modules sits outside the plugin folder, so + // Vite must be allowed to read it, and React must come from the plugin. + const sdkDir = fs.realpathSync(fileURLToPath(new URL("..", import.meta.url))); + const server = { fs: { allow: [pluginDir, sdkDir] } }; + const resolve = { + alias, + dedupe: ["react", "react-dom", "react-i18next", "i18next"], + }; + return { plugins, - resolve: { alias }, + server, + resolve, test: { globals: true, setupFiles: [setupFile], @@ -127,7 +137,8 @@ export function pluginVitestConfig( projects: [ { plugins, - resolve: { alias }, + server, + resolve, test: { name: `${pluginId}:backend`, root: pluginDir, @@ -140,7 +151,8 @@ export function pluginVitestConfig( }, { plugins, - resolve: { alias }, + server, + resolve, test: { name: `${pluginId}:frontend`, root: pluginDir, diff --git a/packages/plugin-sdk/tsconfig.plugin.json b/packages/plugin-sdk/tsconfig.plugin.json index 911890125..2ca748f5a 100644 --- a/packages/plugin-sdk/tsconfig.plugin.json +++ b/packages/plugin-sdk/tsconfig.plugin.json @@ -12,6 +12,7 @@ "moduleDetection": "force", "esModuleInterop": true, "resolveJsonModule": true, + "preserveSymlinks": true, "noEmit": true, "strict": false, "noUnusedLocals": false,