From e8b73623d421a7eaa89d12e87b11d96c59b2c29b Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 3 Jul 2026 17:08:40 -0700 Subject: [PATCH] FN-7477: clarify desktop remote server setup Clarifies the desktop Connection Manager so local mode is distinct from adding saved remote servers. - Split the desktop local server card from the remote server profile flow and keep the editor closed until adding or editing a remote. - Add desktop-specific remote server copy, accessible labels with profile URLs, and responsive styling for the revised layout. - Update Connection Manager tests, native shell docs, localized strings, resource types, and the Fusion changeset. Files changed: .changeset/fn-7477-connection-manager-clarity.md | 7 + docs/native-shell.md | 5 +- .../components/NativeShellConnectionManager.css | 65 ++++++--- .../components/NativeShellConnectionManager.tsx | 153 ++++++++++++-------- .../NativeShellConnectionManager.test.tsx | 156 +++++++++++++++------ packages/i18n/locales/en/app.json | 15 +- packages/i18n/locales/es/app.json | 15 +- packages/i18n/locales/fr/app.json | 15 +- packages/i18n/locales/ko/app.json | 15 +- packages/i18n/locales/zh-CN/app.json | 15 +- packages/i18n/locales/zh-TW/app.json | 15 +- packages/i18n/src/resources.d.ts | 13 +- 12 files changed, 334 insertions(+), 155 deletions(-) Fusion-Task-Id: FN-7477 Fusion-Task-Lineage: adc2df09-d713-4ccd-8fb6-20a32c6a553e Co-authored-by: Fusion (runfusion.ai) --- .../fn-7477-connection-manager-clarity.md | 7 + docs/native-shell.md | 5 +- .../NativeShellConnectionManager.css | 67 ++++--- .../NativeShellConnectionManager.tsx | 155 +++++++++------ .../NativeShellConnectionManager.test.tsx | 176 ++++++++++++------ packages/i18n/locales/en/app.json | 15 +- packages/i18n/locales/es/app.json | 15 +- packages/i18n/locales/fr/app.json | 15 +- packages/i18n/locales/ko/app.json | 15 +- packages/i18n/locales/zh-CN/app.json | 15 +- packages/i18n/locales/zh-TW/app.json | 15 +- packages/i18n/src/resources.d.ts | 13 +- 12 files changed, 346 insertions(+), 167 deletions(-) create mode 100644 .changeset/fn-7477-connection-manager-clarity.md diff --git a/.changeset/fn-7477-connection-manager-clarity.md b/.changeset/fn-7477-connection-manager-clarity.md new file mode 100644 index 0000000000..bcb18981e7 --- /dev/null +++ b/.changeset/fn-7477-connection-manager-clarity.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Clarify the desktop Connection Manager add-remote flow. +category: fix +dev: Desktop Connection Manager now separates Local Server context from saved remote profiles and collapses the remote editor until add/edit. diff --git a/docs/native-shell.md b/docs/native-shell.md index be03bbad3d..df9fc26aaa 100644 --- a/docs/native-shell.md +++ b/docs/native-shell.md @@ -57,11 +57,12 @@ Profiles are first-class saved objects shared by onboarding and Connection Manag Connection Manager supports: -- **Desktop Switch server** shows **Local Server** as a selectable destination in the same list as saved remote/server profiles. Selecting **Local Server** calls `setDesktopMode("local")` and returns the shell to the embedded/local Fusion server without deleting remote profiles. +- **Desktop Switch server** presents the built-in **Local Server** separately from saved **Remote servers**. Local Server is always available in the desktop shell; selecting it calls `setDesktopMode("local")` and returns the shell to the embedded/local Fusion server without deleting remote profiles. +- **Add remote server** is the desktop CTA for saving another Fusion server profile. The remote profile editor stays collapsed until a user chooses **Add remote server** or edits an existing saved profile, so local-only desktop users do not see an empty setup form. - **Use** (activate a saved remote profile). In desktop local mode, using a remote profile first switches desktop mode back to `remote`, then activates the selected profile. - **Edit** (update name/URL/token) - **Delete** -- **Add connection** +- **Mobile Add connection / Scan QR** remains focused on remote profile setup and does not show the desktop-only Local Server guidance. Activation updates `activeProfileId` and stamps `lastUsedAt` on the selected profile. diff --git a/packages/dashboard/app/components/NativeShellConnectionManager.css b/packages/dashboard/app/components/NativeShellConnectionManager.css index 345c428e06..cec1de78f2 100644 --- a/packages/dashboard/app/components/NativeShellConnectionManager.css +++ b/packages/dashboard/app/components/NativeShellConnectionManager.css @@ -2,21 +2,17 @@ width: min(100%, 42rem); } -.native-shell-connection-manager__mode-row { - display: flex; - gap: var(--space-sm); - padding: 0 var(--space-xl) var(--space-md); -} - .native-shell-connection-manager__profiles { display: flex; flex-direction: column; gap: var(--space-sm); padding: 0 var(--space-xl); - max-height: 16rem; + max-height: 18rem; overflow: auto; } +.native-shell-connection-manager__overview, +.native-shell-connection-manager__section-heading, .native-shell-connection-manager__profile { display: flex; align-items: center; @@ -24,40 +20,56 @@ gap: var(--space-md); } -.native-shell-connection-manager__destination { - width: 100%; - border-color: var(--border); - color: var(--text); - text-align: start; - cursor: pointer; -} - -.native-shell-connection-manager__destination:hover, -.native-shell-connection-manager__destination:focus-visible { - border-color: var(--accent-primary); -} - -.native-shell-connection-manager__destination > span:first-child { +.native-shell-connection-manager__overview-copy { display: flex; flex-direction: column; gap: var(--space-xs); } -.native-shell-connection-manager__profile-actions { +.native-shell-connection-manager__overview-copy h3, +.native-shell-connection-manager__section-heading h3, +.native-shell-connection-manager__editor h3 { + margin: 0; +} + +.native-shell-connection-manager__overview-copy p, +.native-shell-connection-manager__section-heading p { + margin: var(--space-xs) 0 0; +} + +.native-shell-connection-manager__section-heading { + padding: var(--space-sm) 0; +} + +.native-shell-connection-manager__profile-actions, +.native-shell-connection-manager__mobile-actions { display: inline-flex; gap: var(--space-sm); + flex-wrap: wrap; +} + +.native-shell-connection-manager__mobile-actions { + padding: var(--space-md) var(--space-xl) 0; } .native-shell-connection-manager__editor { padding-top: var(--space-lg); } +.native-shell-connection-manager__editor h3 { + padding-bottom: var(--space-sm); +} + .native-shell-connection-manager__empty-state { display: flex; flex-direction: column; gap: var(--space-sm); } +.native-shell-connection-manager__empty-state p { + margin: 0; +} + .native-shell-connection-manager__active-pill { display: inline-flex; margin-top: var(--space-xs); @@ -75,13 +87,20 @@ background: color-mix(in srgb, var(--color-warning) 8%, transparent); } +.native-shell-connection-manager__standalone-error { + margin: var(--space-md) var(--space-xl) 0; +} + @media (max-width: 768px) { + .native-shell-connection-manager__overview, + .native-shell-connection-manager__section-heading, .native-shell-connection-manager__profile { flex-direction: column; align-items: flex-start; } - .native-shell-connection-manager__mode-row { - flex-wrap: wrap; + .native-shell-connection-manager__section-heading > .btn, + .native-shell-connection-manager__overview > .btn { + width: 100%; } } diff --git a/packages/dashboard/app/components/NativeShellConnectionManager.tsx b/packages/dashboard/app/components/NativeShellConnectionManager.tsx index 372e32044b..5cb6430074 100644 --- a/packages/dashboard/app/components/NativeShellConnectionManager.tsx +++ b/packages/dashboard/app/components/NativeShellConnectionManager.tsx @@ -24,6 +24,7 @@ export function NativeShellConnectionManager({ open, shellApi, shellState, onClo if (!open) return null; const isAddingConnection = editingProfileId === "__new__"; + const isEditorOpen = editingProfileId !== null; const editingProfile = isAddingConnection ? null : shellState.profiles.find((profile) => profile.id === editingProfileId) ?? activeProfile; @@ -37,6 +38,12 @@ export function NativeShellConnectionManager({ open, shellApi, shellState, onClo setError(null); }; + const startNewRemoteDraft = () => { + setEditingProfileId("__new__"); + setDraft({ name: "", serverUrl: "", authToken: "" }); + setError(null); + }; + const saveCurrent = async () => { setError(null); try { @@ -120,44 +127,57 @@ export function NativeShellConnectionManager({ open, shellApi, shellState, onClo
{isDesktopShell && ( - + <> + {/* + FNXC:DesktopConnectionManager 2026-07-03-16:25: + Desktop Connection Manager must explain that Local Server is built in and remote servers are saved profiles. Keep the remote editor collapsed until the user explicitly adds or edits a remote server so first-run local mode never looks like incomplete setup. + */} +
+
+

{t("shell.localServerTitle", "Local Server")}

+

{t("shell.localServerDescription", "Use the embedded Fusion server on this device.")}

+ {isDesktopLocalActive && {t("shell.activePill", "Active")}} +
+ +
+ +
+
+

{t("shell.remoteServersTitle", "Remote servers")}

+

{t("shell.remoteServersDescription", "Save Fusion servers you want this desktop app to open later.")}

+
+ +
+ )} {shellState.profiles.length === 0 ? (
-

{t("shell.noServersSaved", "No remote servers saved yet.")}

-
- - {shellState.host === "mobile-shell" && ( +

+ {isDesktopShell + ? t("shell.noRemoteServersDesktop", "No remote servers saved yet. Add one only when you want this desktop app to open another Fusion server.") + : t("shell.noServersSaved", "No remote servers saved yet.")} +

+ {!isDesktopShell && ( +
+ - )} -
+
+ )}
) : ( shellState.profiles.map((profile) => ( @@ -171,7 +191,7 @@ export function NativeShellConnectionManager({ open, shellApi, shellState, onClo - - + +
)) )} -
- - {shellState.host === "mobile-shell" && ( + {!isDesktopShell && shellState.profiles.length > 0 && ( +
+ - )} -
+
+ )} -
- - setDraft((value) => ({ ...value, name: event.target.value }))} /> - - setDraft((value) => ({ ...value, serverUrl: event.target.value }))} /> - - setDraft((value) => ({ ...value, authToken: event.target.value }))} /> - {error &&

{error}

} -
+ {error && !isEditorOpen &&

{error}

} + + {isEditorOpen && ( +
+

{isAddingConnection ? t("shell.addRemoteServer", "Add remote server") : t("shell.editRemoteServer", "Edit remote server")}

+ + setDraft((value) => ({ ...value, name: event.target.value }))} /> + + setDraft((value) => ({ ...value, serverUrl: event.target.value }))} /> + + setDraft((value) => ({ ...value, authToken: event.target.value }))} /> + {error &&

{error}

} +
+ )} {deleteCandidate && (
@@ -228,8 +259,12 @@ export function NativeShellConnectionManager({ open, shellApi, shellState, onClo
- - + {isEditorOpen && ( + <> + + + + )}
diff --git a/packages/dashboard/app/components/__tests__/NativeShellConnectionManager.test.tsx b/packages/dashboard/app/components/__tests__/NativeShellConnectionManager.test.tsx index f95d0b7056..b7b4b73934 100644 --- a/packages/dashboard/app/components/__tests__/NativeShellConnectionManager.test.tsx +++ b/packages/dashboard/app/components/__tests__/NativeShellConnectionManager.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { fireEvent, render, screen, waitFor, within } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import { NativeShellConnectionManager } from "../NativeShellConnectionManager"; @@ -6,10 +6,11 @@ function createShellApi() { return { getState: vi.fn(), listProfiles: vi.fn(), - saveProfile: vi.fn(async (input?: { id?: string }) => ({ + saveProfile: vi.fn(async (input?: { id?: string; name?: string; serverUrl?: string; authToken?: string | null }) => ({ id: input?.id ?? "p2", - name: "Prod", - serverUrl: "https://fusion.example.com", + name: input?.name || "Prod", + serverUrl: input?.serverUrl || "https://fusion.example.com", + authToken: input?.authToken ?? null, createdAt: "", updatedAt: "", })), @@ -32,7 +33,37 @@ const remoteProfile = { }; describe("NativeShellConnectionManager", () => { - it("shows a desktop Local Server destination and switches back from an active remote profile", async () => { + it("explains desktop local mode with no remote profiles and hides the editor until add", () => { + const shellApi = createShellApi(); + render( + , + ); + + expect(screen.getByRole("heading", { name: "Local Server" })).toBeInTheDocument(); + expect(screen.getByText("Use the embedded Fusion server on this device.")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Current Local Server" })).toHaveAttribute("aria-pressed", "true"); + expect(screen.getByRole("heading", { name: "Remote servers" })).toBeInTheDocument(); + expect(screen.getByText(/No remote servers saved yet/i)).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Add remote server" })).toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Add connection" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: /^Local$/i })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: /^Remote$/i })).not.toBeInTheDocument(); + expect(screen.queryByLabelText("Name")).not.toBeInTheDocument(); + + fireEvent.click(screen.getByRole("button", { name: "Add remote server" })); + + expect(screen.getByRole("heading", { name: "Add remote server" })).toBeInTheDocument(); + expect(screen.getByLabelText("Name")).toBeInTheDocument(); + expect(screen.getByLabelText("Server URL")).toBeInTheDocument(); + expect(screen.getByLabelText("Auth token (optional)")).toHaveAttribute("type", "password"); + }); + + it("keeps the Local Server destination available from desktop remote mode", async () => { const shellApi = createShellApi(); render( { />, ); - expect(screen.getByRole("button", { name: /Local Server/i })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Local Server" })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Use Local Server" })).toHaveAttribute("aria-pressed", "false"); expect(screen.getByText("Remote")).toBeInTheDocument(); - fireEvent.click(screen.getByRole("button", { name: /Local Server/i })); + fireEvent.click(screen.getByRole("button", { name: "Use Local Server" })); await waitFor(() => expect(shellApi.setDesktopMode).toHaveBeenCalledWith("local")); expect(shellApi.setActiveProfile).not.toHaveBeenCalled(); }); - it("marks Local Server active in desktop local mode and switches remote profile use into remote mode", async () => { + it("separates populated desktop remote profiles from local state and exposes deterministic actions", async () => { const shellApi = createShellApi(); + const duplicateNameProfiles = [ + remoteProfile, + { ...remoteProfile, id: "remote-2", serverUrl: "https://other.example.com" }, + ]; render( , ); - expect(screen.getByRole("button", { name: /Local Server/i })).toHaveAttribute("aria-pressed", "true"); - expect(screen.getByText("Active")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Current Local Server" })).toHaveAttribute("aria-pressed", "true"); + expect(screen.getByRole("button", { name: "Add remote server" })).toBeInTheDocument(); + expect(screen.queryByLabelText("Server URL")).not.toBeInTheDocument(); - fireEvent.click(screen.getAllByLabelText("Use Remote")[1]!); + const firstCard = screen.getByText("https://fusion.example.com").closest(".native-shell-connection-manager__profile"); + expect(firstCard).not.toBeNull(); + expect(within(firstCard as HTMLElement).getByRole("button", { name: "Edit Remote at https://fusion.example.com" })).toBeInTheDocument(); + expect(within(firstCard as HTMLElement).getByRole("button", { name: "Use Remote at https://fusion.example.com" })).toBeInTheDocument(); + expect(within(firstCard as HTMLElement).getByRole("button", { name: "Delete Remote at https://fusion.example.com" })).toBeInTheDocument(); + + fireEvent.click(screen.getByRole("button", { name: "Use Remote at https://other.example.com" })); await waitFor(() => { expect(shellApi.setDesktopMode).toHaveBeenCalledWith("remote"); @@ -83,7 +118,7 @@ describe("NativeShellConnectionManager", () => { expect(shellApi.setDesktopMode.mock.invocationCallOrder[0]).toBeLessThan(shellApi.setActiveProfile.mock.invocationCallOrder[0]); }); - it("renders Local Server deterministically with no remote profiles and an unset desktop mode", () => { + it("validates and saves a new desktop remote profile only after add is chosen", async () => { const shellApi = createShellApi(); render( { />, ); - expect(screen.getByRole("button", { name: /Local Server/i })).toBeInTheDocument(); - expect(screen.getByText("No remote servers saved yet.")).toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Add server" })).toBeInTheDocument(); + fireEvent.click(screen.getByRole("button", { name: "Add remote server" })); + fireEvent.change(screen.getByLabelText("Name"), { target: { value: "Prod" } }); + fireEvent.change(screen.getByLabelText("Server URL"), { target: { value: "ftp://fusion.example.com" } }); + fireEvent.click(screen.getByRole("button", { name: "Save" })); + + expect(await screen.findByRole("alert")).toHaveTextContent("Server URL must use http or https"); + expect(shellApi.saveProfile).not.toHaveBeenCalled(); + + fireEvent.change(screen.getByLabelText("Server URL"), { target: { value: "https://prod.example.com" } }); + fireEvent.change(screen.getByLabelText("Auth token (optional)"), { target: { value: "secret-token" } }); + fireEvent.click(screen.getByRole("button", { name: "Save" })); + + await waitFor(() => { + expect(shellApi.saveProfile).toHaveBeenCalledWith(expect.objectContaining({ + id: undefined, + name: "Prod", + serverUrl: "https://prod.example.com", + authToken: "secret-token", + })); + expect(shellApi.setActiveProfile).toHaveBeenCalledWith("p2"); + }); + expect(screen.queryByText("secret-token")).not.toBeInTheDocument(); }); - it("shows active profile indicator and requires delete confirmation", async () => { + it("edits an existing profile from a collapsed desktop editor", async () => { + const shellApi = createShellApi(); + render( + , + ); + + expect(screen.queryByLabelText("Server URL")).not.toBeInTheDocument(); + fireEvent.click(screen.getByRole("button", { name: "Edit Remote at https://fusion.example.com" })); + expect(screen.getByRole("heading", { name: "Edit remote server" })).toBeInTheDocument(); + fireEvent.change(screen.getByDisplayValue("https://fusion.example.com"), { target: { value: "https://next.example.com" } }); + fireEvent.click(screen.getByRole("button", { name: "Save" })); + + await waitFor(() => { + expect(shellApi.saveProfile).toHaveBeenCalledWith(expect.objectContaining({ id: "remote-1", serverUrl: "https://next.example.com" })); + expect(shellApi.setActiveProfile).toHaveBeenCalledWith("remote-1"); + }); + }); + + it("keeps mobile QR, manual add, active state, and delete confirmation available", async () => { const shellApi = createShellApi(); render( { />, ); - expect(screen.queryByRole("button", { name: /Local Server/i })).toBeNull(); + expect(screen.queryByRole("heading", { name: "Local Server" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Add remote server" })).not.toBeInTheDocument(); expect(screen.getByText("Active")).toBeInTheDocument(); - fireEvent.click(screen.getByLabelText("Delete Prod")); + expect(screen.getByRole("button", { name: "Add connection" })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Scan QR" })).toBeInTheDocument(); + + fireEvent.click(screen.getByRole("button", { name: "Scan QR" })); + + await waitFor(() => { + expect(shellApi.startQrScan).toHaveBeenCalled(); + expect(screen.getByDisplayValue("https://qr.example.com")).toBeInTheDocument(); + }); + + fireEvent.click(screen.getByRole("button", { name: "Delete Prod at https://fusion.example.com" })); expect(screen.getByRole("alertdialog", { name: "Delete server confirmation" })).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Delete" })); @@ -121,27 +209,7 @@ describe("NativeShellConnectionManager", () => { }); }); - it("edits and saves active profile", async () => { - const shellApi = createShellApi(); - render( - , - ); - - fireEvent.change(screen.getByDisplayValue("https://fusion.example.com"), { target: { value: "https://next.example.com" } }); - fireEvent.click(screen.getByText("Save")); - - await waitFor(() => { - expect(shellApi.saveProfile).toHaveBeenCalledWith(expect.objectContaining({ id: "p1", serverUrl: "https://next.example.com" })); - expect(shellApi.setActiveProfile).toHaveBeenCalledWith("p1"); - }); - }); - - it("supports empty-state recovery and QR import without a mobile Local Server option", async () => { + it("keeps mobile empty state focused on QR and manual entry without desktop guidance", async () => { const shellApi = createShellApi(); render( { />, ); - expect(screen.queryByRole("button", { name: /Local Server/i })).toBeNull(); + expect(screen.queryByRole("heading", { name: "Local Server" })).not.toBeInTheDocument(); expect(screen.getByText("No remote servers saved yet.")).toBeInTheDocument(); - fireEvent.click(screen.getAllByText("Scan QR")[0]!); + expect(screen.getByRole("button", { name: "Add server" })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Scan QR" })).toBeInTheDocument(); + expect(screen.queryByLabelText("Server URL")).not.toBeInTheDocument(); - await waitFor(() => { - expect(shellApi.startQrScan).toHaveBeenCalled(); - expect(screen.getByDisplayValue("https://qr.example.com")).toBeInTheDocument(); - }); + fireEvent.click(screen.getByRole("button", { name: "Add server" })); + expect(screen.getByLabelText("Server URL")).toBeInTheDocument(); }); }); diff --git a/packages/i18n/locales/en/app.json b/packages/i18n/locales/en/app.json index de0f51f91a..168ab300c9 100644 --- a/packages/i18n/locales/en/app.json +++ b/packages/i18n/locales/en/app.json @@ -6956,8 +6956,8 @@ "defaultProfileName": "Remote Server", "deleteConfirmLabel": "Delete server confirmation", "deleteConfirmMessage": "Delete {{name}}? This removes the saved profile.", - "deleteProfile": "Delete {{name}}", - "editProfile": "Edit {{name}}", + "deleteProfile": "Delete {{name}} at {{url}}", + "editProfile": "Edit {{name}} at {{url}}", "modeLocal": "Local", "modeRemote": "Remote", "nameLabel": "Name", @@ -6966,10 +6966,17 @@ "serverUrlLabel": "Server URL", "serverUrlProtocolError": "Server URL must use http or https", "use": "Use", - "useProfile": "Use {{name}}", + "useProfile": "Use {{name}} at {{url}}", "localServerTitle": "Local Server", "localServerDescription": "Use the embedded Fusion server on this device.", - "localServerActive": "Current" + "localServerActive": "Current", + "addRemoteServer": "Add remote server", + "editRemoteServer": "Edit remote server", + "remoteServersTitle": "Remote servers", + "remoteServersDescription": "Save Fusion servers you want this desktop app to open later.", + "noRemoteServersDesktop": "No remote servers saved yet. Add one only when you want this desktop app to open another Fusion server.", + "useLocalServer": "Use Local Server", + "currentLocalServer": "Current Local Server" }, "skills": { "addSkill": "Add a skill…", diff --git a/packages/i18n/locales/es/app.json b/packages/i18n/locales/es/app.json index dfbba3c62a..2eb6efea7d 100644 --- a/packages/i18n/locales/es/app.json +++ b/packages/i18n/locales/es/app.json @@ -6946,8 +6946,8 @@ "defaultProfileName": "Servidor remoto", "deleteConfirmLabel": "Confirmación de eliminación del servidor", "deleteConfirmMessage": "¿Eliminar {{name}}? Esto eliminará el perfil guardado.", - "deleteProfile": "Eliminar {{name}}", - "editProfile": "Editar {{name}}", + "deleteProfile": "Eliminar {{name}} en {{url}}", + "editProfile": "Editar {{name}} en {{url}}", "modeLocal": "Local", "modeRemote": "Remoto", "nameLabel": "Nombre", @@ -6956,10 +6956,17 @@ "serverUrlLabel": "URL del servidor", "serverUrlProtocolError": "La URL del servidor debe usar http o https", "use": "Usar", - "useProfile": "Usar {{name}}", + "useProfile": "Usar {{name}} en {{url}}", "localServerTitle": "Servidor local", "localServerDescription": "Usa el servidor Fusion integrado en este dispositivo.", - "localServerActive": "Actual" + "localServerActive": "Actual", + "addRemoteServer": "Añadir servidor remoto", + "editRemoteServer": "Editar servidor remoto", + "remoteServersTitle": "Servidores remotos", + "remoteServersDescription": "Guarda los servidores Fusion que quieras que esta aplicación de escritorio abra más tarde.", + "noRemoteServersDesktop": "Aún no hay servidores remotos guardados. Añade uno solo cuando quieras que esta aplicación de escritorio abra otro servidor Fusion.", + "useLocalServer": "Usar servidor local", + "currentLocalServer": "Servidor local actual" }, "skills": { "addSkill": "Añadir una habilidad…", diff --git a/packages/i18n/locales/fr/app.json b/packages/i18n/locales/fr/app.json index d7390c5702..88c6809298 100644 --- a/packages/i18n/locales/fr/app.json +++ b/packages/i18n/locales/fr/app.json @@ -6946,8 +6946,8 @@ "defaultProfileName": "Serveur distant", "deleteConfirmLabel": "Confirmation de suppression du serveur", "deleteConfirmMessage": "Supprimer {{name}} ? Cela supprimera le profil enregistré.", - "deleteProfile": "Supprimer {{name}}", - "editProfile": "Modifier {{name}}", + "deleteProfile": "Supprimer {{name}} sur {{url}}", + "editProfile": "Modifier {{name}} sur {{url}}", "modeLocal": "Local", "modeRemote": "Distant", "nameLabel": "Nom", @@ -6956,10 +6956,17 @@ "serverUrlLabel": "URL du serveur", "serverUrlProtocolError": "L'URL du serveur doit utiliser http ou https", "use": "Utiliser", - "useProfile": "Utiliser {{name}}", + "useProfile": "Utiliser {{name}} sur {{url}}", "localServerTitle": "Serveur local", "localServerDescription": "Utiliser le serveur Fusion intégré sur cet appareil.", - "localServerActive": "Actuel" + "localServerActive": "Actuel", + "addRemoteServer": "Ajouter un serveur distant", + "editRemoteServer": "Modifier le serveur distant", + "remoteServersTitle": "Serveurs distants", + "remoteServersDescription": "Enregistrez les serveurs Fusion que cette application de bureau doit ouvrir plus tard.", + "noRemoteServersDesktop": "Aucun serveur distant enregistré pour l'instant. Ajoutez-en un uniquement si cette application de bureau doit ouvrir un autre serveur Fusion.", + "useLocalServer": "Utiliser le serveur local", + "currentLocalServer": "Serveur local actuel" }, "skills": { "addSkill": "Ajouter une compétence…", diff --git a/packages/i18n/locales/ko/app.json b/packages/i18n/locales/ko/app.json index 3d5295dd3f..bd67143a2e 100644 --- a/packages/i18n/locales/ko/app.json +++ b/packages/i18n/locales/ko/app.json @@ -6946,8 +6946,8 @@ "defaultProfileName": "원격 서버", "deleteConfirmLabel": "서버 삭제 확인", "deleteConfirmMessage": "{{name}}을(를) 삭제하시겠습니까? 저장된 프로필이 제거됩니다.", - "deleteProfile": "{{name}} 삭제", - "editProfile": "{{name}} 편집", + "deleteProfile": "{{url}}의 {{name}} 삭제", + "editProfile": "{{url}}의 {{name}} 편집", "modeLocal": "로컬", "modeRemote": "원격", "nameLabel": "이름", @@ -6956,10 +6956,17 @@ "serverUrlLabel": "서버 URL", "serverUrlProtocolError": "서버 URL은 http 또는 https를 사용해야 합니다", "use": "사용", - "useProfile": "{{name}} 사용", + "useProfile": "{{url}}의 {{name}} 사용", "localServerTitle": "로컬 서버", "localServerDescription": "이 기기의 내장 Fusion 서버를 사용합니다.", - "localServerActive": "현재" + "localServerActive": "현재", + "addRemoteServer": "원격 서버 추가", + "editRemoteServer": "원격 서버 편집", + "remoteServersTitle": "원격 서버", + "remoteServersDescription": "이 데스크톱 앱에서 나중에 열 Fusion 서버를 저장합니다.", + "noRemoteServersDesktop": "저장된 원격 서버가 없습니다. 이 데스크톱 앱에서 다른 Fusion 서버를 열어야 할 때만 추가하세요.", + "useLocalServer": "로컬 서버 사용", + "currentLocalServer": "현재 로컬 서버" }, "skills": { "addSkill": "스킬 추가…", diff --git a/packages/i18n/locales/zh-CN/app.json b/packages/i18n/locales/zh-CN/app.json index a3e7443cb5..06aa4b480e 100644 --- a/packages/i18n/locales/zh-CN/app.json +++ b/packages/i18n/locales/zh-CN/app.json @@ -6946,8 +6946,8 @@ "defaultProfileName": "远程服务器", "deleteConfirmLabel": "删除服务器确认", "deleteConfirmMessage": "删除 {{name}}?这将移除保存的配置文件。", - "deleteProfile": "删除 {{name}}", - "editProfile": "编辑 {{name}}", + "deleteProfile": "删除 {{url}} 上的 {{name}}", + "editProfile": "编辑 {{url}} 上的 {{name}}", "modeLocal": "本地", "modeRemote": "远程", "nameLabel": "名称", @@ -6956,10 +6956,17 @@ "serverUrlLabel": "服务器 URL", "serverUrlProtocolError": "服务器 URL 必须使用 http 或 https", "use": "使用", - "useProfile": "使用 {{name}}", + "useProfile": "使用 {{url}} 上的 {{name}}", "localServerTitle": "本地服务器", "localServerDescription": "使用此设备上的内置 Fusion 服务器。", - "localServerActive": "当前" + "localServerActive": "当前", + "addRemoteServer": "添加远程服务器", + "editRemoteServer": "编辑远程服务器", + "remoteServersTitle": "远程服务器", + "remoteServersDescription": "保存你希望此桌面应用稍后打开的 Fusion 服务器。", + "noRemoteServersDesktop": "尚未保存远程服务器。仅当你希望此桌面应用打开另一个 Fusion 服务器时再添加。", + "useLocalServer": "使用本地服务器", + "currentLocalServer": "当前本地服务器" }, "skills": { "addSkill": "添加技能…", diff --git a/packages/i18n/locales/zh-TW/app.json b/packages/i18n/locales/zh-TW/app.json index 341992e7cc..c7c46932fd 100644 --- a/packages/i18n/locales/zh-TW/app.json +++ b/packages/i18n/locales/zh-TW/app.json @@ -6946,8 +6946,8 @@ "defaultProfileName": "遠端伺服器", "deleteConfirmLabel": "刪除伺服器確認", "deleteConfirmMessage": "刪除 {{name}}?這將移除已保存的設定檔。", - "deleteProfile": "刪除 {{name}}", - "editProfile": "編輯 {{name}}", + "deleteProfile": "刪除 {{url}} 上的 {{name}}", + "editProfile": "編輯 {{url}} 上的 {{name}}", "modeLocal": "本機", "modeRemote": "遠端", "nameLabel": "名稱", @@ -6956,10 +6956,17 @@ "serverUrlLabel": "伺服器 URL", "serverUrlProtocolError": "伺服器 URL 必須使用 http 或 https", "use": "使用", - "useProfile": "使用 {{name}}", + "useProfile": "使用 {{url}} 上的 {{name}}", "localServerTitle": "本機伺服器", "localServerDescription": "使用此裝置上的內建 Fusion 伺服器。", - "localServerActive": "目前" + "localServerActive": "目前", + "addRemoteServer": "新增遠端伺服器", + "editRemoteServer": "編輯遠端伺服器", + "remoteServersTitle": "遠端伺服器", + "remoteServersDescription": "儲存你希望此桌面應用稍後開啟的 Fusion 伺服器。", + "noRemoteServersDesktop": "尚未儲存遠端伺服器。只有在你希望此桌面應用開啟另一個 Fusion 伺服器時才需要新增。", + "useLocalServer": "使用本機伺服器", + "currentLocalServer": "目前的本機伺服器" }, "skills": { "addSkill": "新增技能…", diff --git a/packages/i18n/src/resources.d.ts b/packages/i18n/src/resources.d.ts index 747a287605..8506205ee5 100644 --- a/packages/i18n/src/resources.d.ts +++ b/packages/i18n/src/resources.d.ts @@ -6951,27 +6951,34 @@ export default interface Resources { "shell": { "activePill": "Active", "addConnection": "Add connection", + "addRemoteServer": "Add remote server", "addServer": "Add server", "authTokenLabel": "Auth token (optional)", "connectionManager": "Connection Manager", "connectionManagerLabel": "Connection Manager", "defaultProfileName": "Remote Server", + "currentLocalServer": "Current Local Server", + "editRemoteServer": "Edit remote server", "deleteConfirmLabel": "Delete server confirmation", "deleteConfirmMessage": "Delete {{name}}? This removes the saved profile.", - "deleteProfile": "Delete {{name}}", - "editProfile": "Edit {{name}}", + "deleteProfile": "Delete {{name}} at {{url}}", + "editProfile": "Edit {{name}} at {{url}}", "localServerActive": "Current", "localServerDescription": "Use the embedded Fusion server on this device.", "localServerTitle": "Local Server", "modeLocal": "Local", "modeRemote": "Remote", "nameLabel": "Name", + "noRemoteServersDesktop": "No remote servers saved yet. Add one only when you want this desktop app to open another Fusion server.", "noServersSaved": "No remote servers saved yet.", + "remoteServersDescription": "Save Fusion servers you want this desktop app to open later.", + "remoteServersTitle": "Remote servers", "scanQr": "Scan QR", "serverUrlLabel": "Server URL", "serverUrlProtocolError": "Server URL must use http or https", "use": "Use", - "useProfile": "Use {{name}}" + "useLocalServer": "Use Local Server", + "useProfile": "Use {{name}} at {{url}}" }, "skills": { "addSkill": "Add a skill…",