-
- {{ c.name }}
+
+
+
+ {{ c.name }}
diff --git a/apps/desktop/src/components/transfer/DataTransferDialog.vue b/apps/desktop/src/components/transfer/DataTransferDialog.vue
index f556d3f4a..e936c2371 100644
--- a/apps/desktop/src/components/transfer/DataTransferDialog.vue
+++ b/apps/desktop/src/components/transfer/DataTransferDialog.vue
@@ -8,6 +8,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import SearchableSelect from "@/components/ui/searchable-select/SearchableSelect.vue";
+import ConnectionGroupBadge from "@/components/connection/ConnectionGroupBadge.vue";
import { useConnectionStore } from "@/stores/connectionStore";
import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
import * as api from "@/lib/backend/api";
@@ -382,6 +383,7 @@ function getConnectionName(id: string) {
+
{{ label }}
@@ -445,6 +447,7 @@ function getConnectionName(id: string) {
+
{{ label }}
diff --git a/apps/desktop/src/composables/useConnectionGroupLabel.ts b/apps/desktop/src/composables/useConnectionGroupLabel.ts
new file mode 100644
index 000000000..400ee6991
--- /dev/null
+++ b/apps/desktop/src/composables/useConnectionGroupLabel.ts
@@ -0,0 +1,16 @@
+import { storeToRefs } from "pinia";
+import { useI18n } from "vue-i18n";
+import { useConnectionStore } from "@/stores/connectionStore";
+
+/** Resolves the sidebar group path label for connection dropdown options. */
+export function useConnectionGroupLabel() {
+ const connectionStore = useConnectionStore();
+ const { connectionGroupPaths } = storeToRefs(connectionStore);
+ const { t } = useI18n();
+
+ function connectionGroupLabel(connectionId: string): string {
+ return connectionGroupPaths.value.get(connectionId)?.join(" / ") || t("connectionGroup.ungroupedLabel");
+ }
+
+ return { connectionGroupPaths, connectionGroupLabel };
+}
diff --git a/apps/desktop/src/stores/__tests__/connectionStore.groupPaths.spec.ts b/apps/desktop/src/stores/__tests__/connectionStore.groupPaths.spec.ts
new file mode 100644
index 000000000..d998d3bb1
--- /dev/null
+++ b/apps/desktop/src/stores/__tests__/connectionStore.groupPaths.spec.ts
@@ -0,0 +1,54 @@
+import { createPinia, setActivePinia, storeToRefs } from "pinia";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import type { SidebarLayout } from "@/types/database";
+
+const groupedLayout: SidebarLayout = {
+ groups: [{ id: "project", name: "Project", collapsed: false }],
+ order: [
+ {
+ type: "group",
+ id: "project",
+ children: [{ type: "connection", id: "grouped" }],
+ },
+ { type: "connection", id: "root" },
+ ],
+};
+
+describe("connectionStore connection group paths", () => {
+ beforeEach(() => {
+ vi.stubGlobal("localStorage", {
+ getItem: vi.fn(() => null),
+ setItem: vi.fn(),
+ removeItem: vi.fn(),
+ });
+ setActivePinia(createPinia());
+ });
+
+ it("shares one cached path map across store consumers", async () => {
+ const { useConnectionStore } = await import("@/stores/connectionStore");
+ const store = useConnectionStore();
+ store.sidebarLayout = groupedLayout;
+
+ const firstConsumer = storeToRefs(store).connectionGroupPaths;
+ const secondConsumer = storeToRefs(store).connectionGroupPaths;
+
+ expect(firstConsumer.value).toBe(secondConsumer.value);
+ expect(firstConsumer.value.get("grouped")).toEqual(["Project"]);
+ expect(firstConsumer.value.get("root")).toEqual([]);
+ });
+
+ it("rebuilds the shared map when the sidebar layout changes", async () => {
+ const { useConnectionStore } = await import("@/stores/connectionStore");
+ const store = useConnectionStore();
+ store.sidebarLayout = groupedLayout;
+ const previousPaths = store.connectionGroupPaths;
+
+ store.sidebarLayout = {
+ groups: [],
+ order: [{ type: "connection", id: "grouped" }],
+ };
+
+ expect(store.connectionGroupPaths).not.toBe(previousPaths);
+ expect(store.connectionGroupPaths.get("grouped")).toEqual([]);
+ });
+});
diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts
index 4cdc37744..12ab61635 100644
--- a/apps/desktop/src/stores/connectionStore.ts
+++ b/apps/desktop/src/stores/connectionStore.ts
@@ -34,6 +34,7 @@ import {
moveConnectionToGroup as moveConnectionToGroupOp,
remapSidebarLayoutConnectionIds,
reorderEntry as reorderEntryOp,
+ buildConnectionGroupPathMap,
type DropPosition,
} from "@/lib/sidebar/sidebarLayout";
import type { SqlCompletionColumn, SqlCompletionForeignKey, SqlCompletionObject, SqlCompletionTable } from "@/lib/sql/sqlCompletion";
@@ -327,6 +328,7 @@ export const useConnectionStore = defineStore("connection", () => {
allDatabases?: boolean;
} | null>(null);
const sidebarLayout = ref
(emptyLayout());
+ const connectionGroupPaths = computed(() => buildConnectionGroupPathMap(sidebarLayout.value));
let layoutPersistTimer: ReturnType | null = null;
const staleTreeRefreshIds = new Set();
const metadataLoadCoordinator = new MetadataLoadCoordinator((event) => {
@@ -5318,6 +5320,7 @@ export const useConnectionStore = defineStore("connection", () => {
markConnectionLost,
recordConnectionLostError,
sidebarLayout,
+ connectionGroupPaths,
getConfig,
connectionIdentifierQuote,
isTreeNodePinned,