mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
feat: finish the homepage plugin, move service links and canvas out of core (B19)
This commit is contained in:
1 parent
21937a6b02
commit
d4ec444558
256 files changed
+27365
-9077
No files matched your search
@@ -23,7 +23,7 @@ import {
|
||||
registerDashboardCard,
|
||||
type DashboardCardRenderProps,
|
||||
} from "@/dashboard/dashboard-cards-registry";
|
||||
import { registerWidget } from "@/features/homepage/widgets/WidgetRegistry";
|
||||
import { registerHomepageWidgetType } from "./homepage-widget-registry";
|
||||
import { registerSettingsComponent } from "@/settings/settings-components";
|
||||
import {
|
||||
declareActionSlot,
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
} from "@/shell/action-registry";
|
||||
import { pluginApiFor, pluginFetch, pluginWsUrl } from "@/lib/plugin-transport";
|
||||
import { registerPluginComponent } from "./component-registry";
|
||||
import type { WidgetTypeDefinition } from "@/types/homepage-types";
|
||||
import type { RegisteredHomepageWidget } from "./homepage-widget-registry";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import {
|
||||
registerLoginMethod,
|
||||
@@ -265,6 +265,7 @@ export function createPluginApp(
|
||||
pluginId,
|
||||
titleKey: key(card.titleKey),
|
||||
defaultHeight: card.defaultHeight,
|
||||
defaultPanel: card.defaultPanel,
|
||||
component: scoped(
|
||||
card.component as unknown as ComponentType<DashboardCardRenderProps>,
|
||||
),
|
||||
@@ -274,13 +275,14 @@ export function createPluginApp(
|
||||
|
||||
registerHomepageWidget(widget) {
|
||||
return track(
|
||||
registerWidget({
|
||||
registerHomepageWidgetType({
|
||||
...widget,
|
||||
pluginId,
|
||||
component: scoped(widget.component),
|
||||
editFormComponent: widget.editFormComponent
|
||||
? scoped(widget.editFormComponent)
|
||||
: undefined,
|
||||
} as unknown as WidgetTypeDefinition),
|
||||
} as unknown as RegisteredHomepageWidget),
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { enabledHostProtocols } from "@/sidebar/host-protocols";
|
||||
import { useHostActions } from "@/sidebar/host-contributions";
|
||||
import { tabTypeForActivity, useTabTypes } from "@/shell/tab-registry";
|
||||
import {
|
||||
getHomepageWidgetType,
|
||||
useHomepageWidgetTypes,
|
||||
} from "./homepage-widget-registry";
|
||||
import { useTranslation as useI18nTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
__setPluginHost,
|
||||
type HomepageWidgetContribution,
|
||||
type HostActionContribution,
|
||||
type PluginHostBridge,
|
||||
type PluginHostRecord,
|
||||
type SettingsScope,
|
||||
@@ -294,6 +302,29 @@ export const pluginHostBridge: PluginHostBridge = {
|
||||
...(entry.reason ? { reason: entry.reason } : {}),
|
||||
};
|
||||
},
|
||||
|
||||
useHostActions: () => useHostActions() as unknown as HostActionContribution[],
|
||||
|
||||
useActivityTypes: () => {
|
||||
const defs = useTabTypes();
|
||||
const types = new Set(["file_manager"]);
|
||||
for (const def of defs) {
|
||||
for (const type of def.activityTypes ?? []) types.add(type);
|
||||
}
|
||||
return [...types];
|
||||
},
|
||||
|
||||
activityTarget: (type) => {
|
||||
const def = tabTypeForActivity(type);
|
||||
if (!def) return undefined;
|
||||
return { icon: def.icon, tab: def.id, titleKey: def.titleKey };
|
||||
},
|
||||
|
||||
useHomepageWidgetTypes: () =>
|
||||
useHomepageWidgetTypes() as unknown as HomepageWidgetContribution[],
|
||||
homepageWidgetType: (id) =>
|
||||
getHomepageWidgetType(id) as unknown as
|
||||
HomepageWidgetContribution | undefined,
|
||||
};
|
||||
|
||||
/** Installs the bridge. Idempotent. */
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { ComponentType } from "react";
|
||||
import { createRegistry } from "@/lib/registry";
|
||||
|
||||
/**
|
||||
* A homepage widget type any plugin contributes through
|
||||
* app.registerHomepageWidget. Core owns this registry so the homepage
|
||||
* plugin's canvas can read it without every widget-contributing plugin
|
||||
* depending on homepage being enabled.
|
||||
*/
|
||||
export interface RegisteredHomepageWidget {
|
||||
id: string;
|
||||
pluginId?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: "links" | "info" | "system" | "monitoring";
|
||||
icon: React.ReactNode;
|
||||
defaultConfig: Record<string, unknown>;
|
||||
defaultSize: { w: number; h: number };
|
||||
minSize: { w: number; h: number };
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
component: ComponentType<any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
editFormComponent?: ComponentType<any>;
|
||||
}
|
||||
|
||||
const registry = createRegistry<RegisteredHomepageWidget>();
|
||||
|
||||
export const registerHomepageWidgetType = registry.register;
|
||||
export const unregisterHomepageWidgetType = registry.unregister;
|
||||
export const getHomepageWidgetType = registry.get;
|
||||
export const listHomepageWidgetTypes = registry.list;
|
||||
export const useHomepageWidgetTypes = registry.useList;
|
||||
export const resetHomepageWidgetTypes = registry.reset;
|
||||
@@ -74,6 +74,7 @@ export { ActionSlot, ComponentSlot } from "@/shell/ActionSlot";
|
||||
// Components other plugins offer by id (app.registerComponent), rendered with
|
||||
// a fallback while their plugin is off.
|
||||
export { PluginComponent } from "@/plugin-host/component-registry";
|
||||
export { PluginViewPlaceholder } from "@/plugin-host/PluginViewPlaceholder";
|
||||
|
||||
// More primitives and hooks.
|
||||
export { cn } from "@/lib/utils";
|
||||
@@ -90,8 +91,8 @@ export {
|
||||
useUiPreferencesContext,
|
||||
} from "@/contexts/UiPreferencesContext";
|
||||
// Homepage widget pieces, for plugins that register a widget.
|
||||
export { WidgetTitle } from "@/features/homepage/widgets/WidgetTitle";
|
||||
export { runVisibleInterval } from "@/features/homepage/use-visible-interval";
|
||||
export { WidgetTitle } from "@/lib/widget-title";
|
||||
export { runVisibleInterval } from "@/lib/visible-interval";
|
||||
export { useIsMobile } from "@/hooks/use-mobile";
|
||||
export { PassphraseDialog } from "@/ssh/dialogs/PassphraseDialog";
|
||||
export { OPKSSHDialog } from "@/ssh/dialogs/OPKSSHDialog";
|
||||
@@ -138,7 +139,18 @@ export { useConnectionDefaults } from "@/contexts/ConnectionDefaultsContext";
|
||||
// password for sudo autofill, the open tab record, the user's keybindings and
|
||||
// host terminal options.
|
||||
export { getCookie } from "@/main-axios";
|
||||
export { logActivity } from "@/api/dashboard-api";
|
||||
export {
|
||||
logActivity,
|
||||
getRecentActivity,
|
||||
getUptime,
|
||||
type RecentActivityItem,
|
||||
type UptimeInfo,
|
||||
} from "@/api/dashboard-api";
|
||||
export {
|
||||
getVersionInfo,
|
||||
getDatabaseHealth,
|
||||
type VersionInfo,
|
||||
} from "@/api/system-status-api";
|
||||
export { getHostPassword } from "@/api/credentials-api";
|
||||
export {
|
||||
patchOpenTab,
|
||||
|
||||
Reference in new issue
Block a user