mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
feat: add a permission-gated UI action registry and action slots for plugins
Moves the terminal toolbar's hardwired AI button into a slot the ssh-terminal plugin offers and the ai plugin fills, gated on ai.services.use.
This commit is contained in:
1 parent
225f618337
commit
2d6c1cdecb
29 files changed
+1971
-40
No files matched your search
@@ -13,26 +13,56 @@
|
||||
* tabUtils.tsx (railView === "ai", case "ai"), the same way the terminal's
|
||||
* tab is baked into Terminal.tsx rather than going through the generic tab
|
||||
* registry. What gates it is isTabTypeAvailable("ai") in
|
||||
* src/ui/shell/pluginLoader.ts's BUILT_IN_TABS_BY_PLUGIN map, which is
|
||||
* consulted directly by AppShell.tsx, CommandPalette.tsx and HostItem.tsx --
|
||||
* the same live mechanism ssh-terminal's "terminal" entry already uses.
|
||||
* src/ui/shell/pluginLoader.ts's BUILT_IN_TABS_BY_PLUGIN map.
|
||||
*
|
||||
* register()/unregister() exist for parity with the other first-party
|
||||
* plugins' frontend halves. Nothing invokes them yet: there is no frontend
|
||||
* plugin loader (see src/ui/tests/sidebar/plugin-extension-seam.test.tsx),
|
||||
* and the "ai" surface does not need registerRailItem/registerTabComponent
|
||||
* in the first place, since it is gated through BUILT_IN_TABS_BY_PLUGIN
|
||||
* instead.
|
||||
* What this file DOES contribute is the terminal toolbar's AI button, through
|
||||
* the action registry: ai.openWithContext, declared in manifest.json and
|
||||
* gated on ai.services.use. The button used to be hardwired into
|
||||
* TerminalToolbar.tsx behind a showAiAssistant prop.
|
||||
*
|
||||
* There is still no frontend plugin loader, so nothing imports this file yet
|
||||
* (see src/ui/tests/sidebar/plugin-extension-seam.test.tsx). Until one exists,
|
||||
* pluginLoader.ts performs the same registration itself when the ai plugin is
|
||||
* enabled. This file is the version that runs once a loader lands, and the two
|
||||
* must be kept in step.
|
||||
*/
|
||||
|
||||
export const id = "ai";
|
||||
export const tabId = "ai";
|
||||
export const actionId = "ai.openWithContext";
|
||||
|
||||
export async function register() {
|
||||
// Nothing to register: "ai" is a built-in rail-view/tab gated through
|
||||
// isTabTypeAvailable, not a runtime-registered surface. See the header.
|
||||
/** Asks the focused terminal to open its AI panel seeded with `context`. */
|
||||
export function openWithContext(context) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("termix:ai:openWithContext", {
|
||||
detail: { context: typeof context === "string" ? context : "" },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function unregister() {
|
||||
// Nothing to unregister; see register().
|
||||
export async function register({
|
||||
registerAction,
|
||||
registerSlotContribution,
|
||||
icons,
|
||||
}) {
|
||||
registerAction(actionId, openWithContext, {
|
||||
permission: "ai.services.use",
|
||||
pluginId: id,
|
||||
});
|
||||
|
||||
registerSlotContribution("terminal.toolbar", {
|
||||
actionId,
|
||||
titleKey: "ai.assistant",
|
||||
icon: icons.Bot,
|
||||
kind: "button",
|
||||
pluginId: id,
|
||||
});
|
||||
}
|
||||
|
||||
export async function unregister({
|
||||
unregisterAction,
|
||||
unregisterSlotContribution,
|
||||
}) {
|
||||
unregisterSlotContribution("terminal.toolbar", actionId);
|
||||
unregisterAction(actionId);
|
||||
}
|
||||
@@ -36,7 +36,18 @@
|
||||
"defaultForRole": {
|
||||
"admin": ["ai.services.use"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"id": "ai.openWithContext",
|
||||
"titleKey": "ai.assistant",
|
||||
"handler": "openWithContext",
|
||||
"icon": "Bot",
|
||||
"permission": "ai.services.use",
|
||||
"slot": "terminal.toolbar",
|
||||
"kind": "button"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sidecars": []
|
||||
}
|
||||
@@ -12,10 +12,12 @@
|
||||
|
||||
export const id = "ssh-terminal";
|
||||
export const tabId = "terminal";
|
||||
export const toolbarSlotId = "terminal.toolbar";
|
||||
|
||||
export async function register({
|
||||
registerRailItem,
|
||||
registerTabComponent,
|
||||
declareActionSlot,
|
||||
icons,
|
||||
}) {
|
||||
registerTabComponent(tabId, () =>
|
||||
@@ -30,12 +32,18 @@ export async function register({
|
||||
labelKey: "nav.terminal",
|
||||
kind: "tab",
|
||||
});
|
||||
|
||||
// Offers other plugins a place in the terminal toolbar. The terminal does
|
||||
// not know or care who fills it.
|
||||
declareActionSlot({ id: toolbarSlotId, accepts: ["button"] });
|
||||
}
|
||||
|
||||
export async function unregister({
|
||||
unregisterRailItem,
|
||||
unregisterTabComponent,
|
||||
undeclareActionSlot,
|
||||
}) {
|
||||
unregisterRailItem(tabId);
|
||||
unregisterTabComponent(tabId);
|
||||
undeclareActionSlot(toolbarSlotId);
|
||||
}
|
||||
@@ -24,6 +24,12 @@
|
||||
"icon": "SquareTerminal",
|
||||
"openFrom": ["rail", "host-context-menu", "palette"]
|
||||
}
|
||||
],
|
||||
"actionSlots": [
|
||||
{
|
||||
"id": "terminal.toolbar",
|
||||
"accepts": ["button"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"sidecars": []
|
||||
|
||||
Reference in new issue
Block a user