- {t("onboarding.pluginsIntro")}
+ {t("onboarding.pluginsIntro")}{" "}
+
+ {t("onboarding.pluginsDocsLink")}
+
diff --git a/src/ui/plugin-host/app.ts b/src/ui/plugin-host/app.ts
index 6ae583bd6..100b8d560 100644
--- a/src/ui/plugin-host/app.ts
+++ b/src/ui/plugin-host/app.ts
@@ -72,6 +72,7 @@ import {
} from "./host-scope";
import { manifestDeclares, type ViewKind } from "./view-ownership";
import { pluginKey } from "@/lib/plugin-i18n";
+import { pluginDocsPage, pluginDocsUrl } from "@termix-ssh/plugin-sdk/docs";
import { hasPermission } from "@/hooks/use-permissions";
import i18n from "@/i18n/i18n";
@@ -83,6 +84,20 @@ export interface PluginAppHandle {
dispose: () => void;
}
+function createDocsApi(manifest: PluginManifest): TermixApp["docs"] {
+ const url = pluginDocsUrl(manifest?.docs);
+ const page = (name?: string, anchor?: string) =>
+ url ? pluginDocsPage(url, name, anchor) : null;
+ return {
+ url,
+ page,
+ open(name, anchor) {
+ const target = page(name, anchor);
+ if (target) window.open(target, "_blank", "noopener,noreferrer");
+ },
+ };
+}
+
/**
* Builds the object a plugin's `activate(app)` receives.
*
@@ -602,6 +617,8 @@ export function createPluginApp(
confirm: (options) => shell.confirm!(options),
+ docs: createDocsApi(manifest),
+
desktop: {
available: isElectron(),
remoteServerUrl,
diff --git a/src/ui/plugin-host/bridge.ts b/src/ui/plugin-host/bridge.ts
index adc3e6c97..53cb693fb 100644
--- a/src/ui/plugin-host/bridge.ts
+++ b/src/ui/plugin-host/bridge.ts
@@ -39,6 +39,7 @@ import {
} from "@/api/open-tabs-api";
import { runKeybindingAction } from "@/shell/keybinding-registry";
import { usePluginScope } from "./scope";
+import { usePluginDocsUrl } from "@/components/docs-link";
import { knownPluginIds, usePluginStore } from "./plugin-store";
import { useUiPreferencesContext } from "@/contexts/UiPreferencesContext";
import type { UiPluginPresets } from "@/types/ui-preferences";
@@ -225,6 +226,10 @@ export const pluginHostBridge: PluginHostBridge = {
return pluginId;
},
+ useDocsUrl(pluginId, page, anchor) {
+ return usePluginDocsUrl(pluginId, page, anchor);
+ },
+
useTranslation(pluginId) {
const { t, i18n } = useI18nTranslation(pluginId);
return {
diff --git a/src/ui/plugin-host/sdk-ui.ts b/src/ui/plugin-host/sdk-ui.ts
index b3149908e..78fd103b4 100644
--- a/src/ui/plugin-host/sdk-ui.ts
+++ b/src/ui/plugin-host/sdk-ui.ts
@@ -289,6 +289,8 @@ export {
type PanelDensity,
type PanelViewMode,
} from "@/components/panel-layout";
+// A link to docs. Inside a plugin, page="setup" opens that page of its docs.
+export { DocsLink, type DocsLinkProps } from "@/components/docs-link";
export {
AddButton,
FormFooter,
diff --git a/src/ui/plugins/PluginDetail.tsx b/src/ui/plugins/PluginDetail.tsx
index 37e391a2d..294094c72 100644
--- a/src/ui/plugins/PluginDetail.tsx
+++ b/src/ui/plugins/PluginDetail.tsx
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import {
+ BookOpen,
Bug,
Check,
Database,
@@ -262,6 +263,40 @@ export function PluginDetail({
+ {plugin.env.length > 0 && (
+
+
+ {plugin.env.map((v) => (
+
+
+
+ {v.name}
+
+ {v.required && (
+
+ {t("plugins.manager.envRequired")}
+
+ )}
+ {v.default !== undefined && (
+
+ {t("plugins.manager.envDefault", {
+ value: v.default,
+ })}
+
+ )}
+
+
+ {v.description}
+
+
+ ))}
+
+
+ )}
+
{plugin.installed && (
@@ -419,6 +454,20 @@ export function PluginDetail({
)}
+ {plugin.docs && (
+
+
+
+ {t("plugins.manager.documentation")}
+
+
+ )}
+
{hasSettings && (
}
title={t("nav.plugins")}
+ docs={docsUrl("plugins")}
status={t("plugins.manager.status.summary", {
installed: installed.length,
running,
@@ -550,13 +553,21 @@ function InstalledCard({
: t("plugins.manager.enable")}
)}
+
diff --git a/src/ui/sidebar/UserProfilePanel.tsx b/src/ui/sidebar/UserProfilePanel.tsx
index 012cebaa6..00810674d 100644
--- a/src/ui/sidebar/UserProfilePanel.tsx
+++ b/src/ui/sidebar/UserProfilePanel.tsx
@@ -76,6 +76,7 @@ import {
useFeatureSettings,
type FeatureSectionId,
} from "@/settings/FeatureSettingsSections";
+import { docsUrl } from "@/lib/docs";
type UserProfileSection =
| "account"
@@ -1345,7 +1346,7 @@ export function UserProfilePanel({