feat: move session sharing and collab rooms into a plugin (B12)

This commit is contained in:
LukeGus committed 2026-09-23 23:42:46 -05:00
1 parent 791d8656a1
commit a276f6898c
207 files changed
+35771 -11072

No files matched your search

+6
View File
@@ -60,6 +60,12 @@
"editorTab": "telnet"
}
],
"actionSlots": [
{
"id": "remote-desktop.toolbar",
"accepts": ["button"]
}
],
"guest": true
},
"locales": "locales",
@@ -45,7 +45,6 @@ import {
ConnectionLogProvider,
useConnectionLog,
} from "@/ssh/connection-log/ConnectionLogContext.tsx";
import { ShareSessionModal } from "@/features/session-sharing/ShareSessionModal.tsx";
import type { SSHHost } from "@/types";
import { useConnectionDefaults } from "@/contexts/ConnectionDefaultsContext";
import { resolveConnectionDefaults } from "@/lib/connection-defaults";
@@ -68,8 +67,6 @@ export type GuacamoleQuickHost = GuacamoleAppInnerProps["hostConfig"] & {
export interface GuacamoleAppHandle {
disconnect: () => void;
isConnected: () => boolean;
openShareModal: () => void;
canShare: () => boolean;
/** Reconnects, from the tab bar's refresh. */
refresh: () => void;
}
@@ -202,7 +199,6 @@ const GuacamoleAppInner = React.forwardRef<
id: string;
origin: ConnectionOrigin;
} | null>(null);
const [shareModalOpen, setShareModalOpen] = useState(false);
const [error, setError] = useState<string | null>(null);
const [connectionError, setConnectionError] = useState<string | null>(null);
const [isDisplayReady, setIsDisplayReady] = useState(false);
@@ -299,8 +295,6 @@ const GuacamoleAppInner = React.forwardRef<
useImperativeHandle(ref, () => ({
disconnect: () => displayRef.current?.disconnect(),
isConnected: () => displayRef.current?.isConnected() === true,
openShareModal: () => setShareModalOpen(true),
canShare: () => guacamoleConnectionId !== null,
refresh: () => reconnectRef.current(),
}));
@@ -649,16 +643,16 @@ const GuacamoleAppInner = React.forwardRef<
onTouchModeChange={setTouchMode}
zoom={displayZoom}
onHide={() => setToolbarHidden(true)}
/>
)}
{shareModalOpen && guacamoleConnectionId && (
<ShareSessionModal
open={shareModalOpen}
onClose={() => setShareModalOpen(false)}
hostId={hostId}
sessionId={guacamoleConnectionId}
protocol={resolvedProtocol}
tabInstanceId={tabId}
slotContext={
typeof hostId === "number"
? {
hostId,
sessionId: guacamoleConnectionId,
protocol: resolvedProtocol,
tabInstanceId: tabId,
}
: undefined
}
/>
)}
</div>
@@ -32,6 +32,7 @@ import type {
GuacamoleTouchMode,
} from "./GuacamoleDisplay.tsx";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { ActionSlot } from "@termix/plugin-sdk/ui";
import { cn } from "@/lib/utils";
import {
detectRuntimeMetaKeyFamily,
@@ -50,6 +51,19 @@ interface GuacamoleToolbarProps {
zoom?: number;
onHide?: () => void;
metaKeyFamily?: MetaKeyFamily;
/** Handed to "remote-desktop.toolbar" actions (the share button). */
slotContext?: RemoteDesktopToolbarContext;
}
export const REMOTE_DESKTOP_TOOLBAR_SLOT = "remote-desktop.toolbar";
/** What a "remote-desktop.toolbar" action is invoked with. */
export interface RemoteDesktopToolbarContext {
hostId: number;
/** guacd's connection id, once the session is up. */
sessionId: string | null;
protocol: "rdp" | "vnc" | "telnet";
tabInstanceId?: string;
}
const MODIFIER_KEYSYMS = {
@@ -140,6 +154,7 @@ export const GuacamoleToolbar: React.FC<GuacamoleToolbarProps> = ({
zoom = 1,
onHide,
metaKeyFamily,
slotContext,
}) => {
const { t } = useTranslation();
const meta = metaKeyLabels(
@@ -574,6 +589,16 @@ export const GuacamoleToolbar: React.FC<GuacamoleToolbarProps> = ({
</div>
</div>
{slotContext && (
<ActionSlot
slotId={REMOTE_DESKTOP_TOOLBAR_SLOT}
className={cn(BTN_ICON)}
hideLabels
when={{ ...slotContext }}
context={() => [slotContext]}
/>
)}
{/* Collapse */}
<div className="w-px h-5 bg-border mx-0.5 shrink-0" />
<Tooltip>
@@ -27,6 +27,7 @@ import {
} from "./HostEditorGuacamoleTabs";
import { getGuacamoleTokenFromHost } from "./guacamole-api";
import { quickConnectGuacHost } from "./quick-connect-guac-host";
import { REMOTE_DESKTOP_TOOLBAR_SLOT } from "./GuacamoleToolbar.tsx";
type Protocol = "rdp" | "vnc" | "telnet";
@@ -220,6 +221,12 @@ export function activate(app: TermixApp): void {
});
if (app.guest) return;
// The share button lives here, contributed by session sharing.
app.declareActionSlot({
id: REMOTE_DESKTOP_TOOLBAR_SLOT,
accepts: ["button"],
});
// Mints a token for presenting a host in a collab room.
app.registerAction(
"session.remoteDisplay.token",
@@ -4,7 +4,8 @@ import { describe, expect, it, vi } from "vitest";
import { GuacamoleToolbar } from "../../src/frontend/GuacamoleToolbar.js";
import type { GuacamoleDisplayHandle } from "../../src/frontend/GuacamoleDisplay.js";
vi.mock("react-i18next", () => ({
vi.mock("react-i18next", async (importOriginal) => ({
...(await importOriginal<typeof import("react-i18next")>()),
useTranslation: () => ({ t: (key: string) => key }),
}));