diff --git a/.changeset/fn-006-remote-url-label.md b/.changeset/fn-006-remote-url-label.md new file mode 100644 index 0000000000..53d84b205b --- /dev/null +++ b/.changeset/fn-006-remote-url-label.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Remote Access now labels a Cloudflare tunnel URL correctly instead of calling it a Tailnet URL. +category: fix +dev: RemoteSection derives the share-block label from remoteStatus.provider; adds settings.remote.cloudflareTunnelURL and settings.remote.tunnelURL. diff --git a/packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx b/packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx index dacba55a00..73a8613a55 100644 --- a/packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx +++ b/packages/dashboard/app/components/__tests__/SettingsModal.remote-notifications.test.tsx @@ -517,14 +517,38 @@ describe("SettingsModal", () => { expect(document.querySelector(".remote-share-block")).not.toBeInTheDocument(); }); - it("renders remote-share-block when tunnel is running with a URL", async () => { + it("labels a running Cloudflare tunnel URL with Cloudflare terminology", async () => { + mockFetchRemoteStatus.mockResolvedValue({ provider: "cloudflare", state: "running", url: "https://demo-tunnel.trycloudflare.com/", lastError: null }); + await renderModalSection("remote", "Remote Access"); + + const shareBlock = document.querySelector(".remote-share-block"); + expect(shareBlock).toBeInTheDocument(); + expect(shareBlock?.textContent).toContain("Cloudflare tunnel URL:"); + expect(shareBlock?.textContent).toContain("https://demo-tunnel.trycloudflare.com/"); + expect(shareBlock?.textContent).not.toContain("Tailnet"); + }); + + it("retains the Tailnet URL label for a running Tailscale tunnel", async () => { mockFetchRemoteStatus.mockResolvedValue({ provider: "tailscale", state: "running", url: "https://machine.ts.net/", lastError: null }); await renderModalSection("remote", "Remote Access"); const statusBar = document.querySelector(".remote-status-bar"); + const shareBlock = document.querySelector(".remote-share-block"); expect(statusBar).toBeInTheDocument(); expect(statusBar?.className).toContain("remote-status-bar--running"); - expect(document.querySelector(".remote-share-block")).toBeInTheDocument(); + expect(shareBlock?.textContent).toContain("Tailnet URL:"); + expect(shareBlock?.textContent).toContain("https://machine.ts.net/"); + }); + + it("uses a neutral URL label when the running tunnel provider is unknown", async () => { + mockFetchRemoteStatus.mockResolvedValue({ provider: null, state: "running", url: "https://unknown.example/", lastError: null }); + await renderModalSection("remote", "Remote Access"); + + const shareBlock = document.querySelector(".remote-share-block"); + expect(shareBlock).toBeInTheDocument(); + expect(shareBlock?.textContent).toContain("Tunnel URL:"); + expect(shareBlock?.textContent).toContain("https://unknown.example/"); + expect(shareBlock?.textContent).not.toContain("Tailnet"); }); it("updates provider selection via radio and shows provider status", async () => { diff --git a/packages/dashboard/app/components/settings/sections/RemoteSection.tsx b/packages/dashboard/app/components/settings/sections/RemoteSection.tsx index 3125ab3008..fbfeee28b7 100644 --- a/packages/dashboard/app/components/settings/sections/RemoteSection.tsx +++ b/packages/dashboard/app/components/settings/sections/RemoteSection.tsx @@ -122,22 +122,31 @@ export function RemoteSection({ form, setForm, remote }: RemoteSectionProps) { )} {tunnelState === "running" && (remoteStatus?.url || tunnelShareLink) && (() => { let accessCode: string | null = null; - let tailnetUrl: string | null = remoteStatus?.url ?? null; + let shareUrl: string | null = remoteStatus?.url ?? null; if (tunnelShareLink?.url) { try { const parsed = new URL(tunnelShareLink.url); accessCode = parsed.searchParams.get("rt"); - if (!tailnetUrl) - tailnetUrl = `${parsed.origin}/`; + if (!shareUrl) + shareUrl = `${parsed.origin}/`; } catch { // fall through } } + /* + FNXC:RemoteAccess 2026-08-18-07:10: + The share-block URL label must name the provider that is actually running (`remoteStatus.provider`), not the radio selection in `form` — the radio can hold an unsaved provider while a tunnel from the other provider is still up. A Cloudflare tunnel URL was previously labelled "Tailnet URL:", applying Tailscale vocabulary to a trycloudflare.com address. Unknown/null provider falls back to neutral "Tunnel URL:" so the row never asserts a provider it cannot prove. + */ + const shareUrlLabel = remoteStatus?.provider === "cloudflare" + ? t("settings.remote.cloudflareTunnelURL", "Cloudflare tunnel URL:") + : remoteStatus?.provider === "tailscale" + ? t("settings.remote.tailnetURL", "Tailnet URL:") + : t("settings.remote.tunnelURL", "Tunnel URL:"); return (
{tailnetUrl}
+ {shareUrl && ({shareUrl}