Skip to content

Commit f786200

Browse files
committed
fix(servers): always show clone/add-account option, resolve to original source
The clone menu item was hidden for servers adopted into user-config sync (and for clones themselves), so it disappeared intermittently. Clone is now always available; cloning a clone resolves to its original source instead of stacking clone-of-clone chains. Signed-off-by: crimsonsunset <jsangio1@gmail.com>
1 parent 150bb3e commit f786200

2 files changed

Lines changed: 23 additions & 12 deletions

File tree

apps/desktop/src/features/servers/ServerActionMenu.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ export interface ServerActionMenuProps {
3939
hasUpdateAvailable?: boolean;
4040
/** Latest registry version when an update is available. */
4141
latestVersion?: string | null;
42-
/** Show "Add another account…" for registry/manual installs (not clones-of-clones). */
43-
canCloneAccount?: boolean;
4442
onConfigure: () => void;
4543
onRefresh: () => void;
4644
onReconnect: () => void;
@@ -67,7 +65,6 @@ export function ServerActionMenu({
6765
updatePolicy = 'notify',
6866
hasUpdateAvailable = false,
6967
latestVersion,
70-
canCloneAccount = false,
7168
onConfigure,
7269
onRefresh,
7370
onReconnect,
@@ -175,7 +172,7 @@ export function ServerActionMenu({
175172
onSelect={onViewDefinition}
176173
data-testid={`view-definition-${serverId}`}
177174
/>
178-
{canCloneAccount && onCloneAccount && (
175+
{onCloneAccount && (
179176
<DropdownMenuAction
180177
icon={Copy}
181178
label={t('actions.cloneAccount')}

apps/desktop/src/features/servers/ServersPage.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,28 @@ function getInstalledCloneLineage(state: InstalledServerState): string | undefin
8888
}
8989

9090
/**
91-
* Whether the overflow menu should offer "Add another account…".
91+
* Resolve the original (non-clone) source server for a clone request. Cloning is available
92+
* on every installed server, including clones — cloning a clone clones its original source
93+
* instead of stacking clone-of-clone chains. Falls back to the clicked server if its source
94+
* can no longer be found (e.g. the original was uninstalled).
9295
*/
93-
function canCloneServer(server: ServerViewModelWithClone): boolean {
94-
if (server.cloned_from) {
95-
return false;
96+
function resolveCloneSource(
97+
server: ServerViewModelWithClone,
98+
allServers: ServerViewModelWithClone[]
99+
): ServerViewModelWithClone {
100+
const visited = new Set<string>();
101+
let current = server;
102+
103+
while (current.cloned_from && !visited.has(current.id)) {
104+
visited.add(current.id);
105+
const source = allServers.find((candidate) => candidate.id === current.cloned_from);
106+
if (!source) {
107+
return current;
108+
}
109+
current = source;
96110
}
97111

98-
const sourceType = server.installation_source?.type;
99-
return sourceType === 'registry' || sourceType === 'manual_entry';
112+
return current;
100113
}
101114

102115
// Helper to merge definitions with states (same as registryStore)
@@ -1943,7 +1956,6 @@ export function ServersPage() {
19431956
})
19441957
}
19451958
latestVersion={server.latest_available_version}
1946-
canCloneAccount={canCloneServer(server)}
19471959
onConfigure={() => handleConfigureClick(server)}
19481960
onRefresh={() => handleRefresh(server)}
19491961
onReconnect={() => handleReconnect(server)}
@@ -1952,7 +1964,9 @@ export function ServersPage() {
19521964
onLockToCurrentVersion={() => handleLockToCurrentVersion(server)}
19531965
onViewLogs={() => setLogViewerServer({ id: server.id, name: server.name })}
19541966
onViewDefinition={() => setDefinitionServer({ id: server.id, name: server.name })}
1955-
onCloneAccount={() => setCloneModalServer(server)}
1967+
onCloneAccount={() =>
1968+
setCloneModalServer(resolveCloneSource(server, installedServers))
1969+
}
19561970
onUninstall={() => handleUninstall(server)}
19571971
/>
19581972
</div>

0 commit comments

Comments
 (0)