diff --git a/frontend/src/components/Sidebar.locate-toolbar.test.tsx b/frontend/src/components/Sidebar.locate-toolbar.test.tsx
index 061af512..18680dab 100644
--- a/frontend/src/components/Sidebar.locate-toolbar.test.tsx
+++ b/frontend/src/components/Sidebar.locate-toolbar.test.tsx
@@ -1034,6 +1034,8 @@ describe('Sidebar locate toolbar', () => {
expect(css).toMatch(/\.gn-v2-tree-title \{[^}]*font-size: var\(--gn-sidebar-tree-font-size, var\(--gn-font-size-sm, 12px\)\);/s);
expect(css).toMatch(/\.gn-v2-tree-title\.is-mono \.gn-v2-tree-label \{[^}]*font-size: inherit;[^}]*font-weight: 400 !important;/s);
expect(css).toMatch(/\.gn-v2-tree-count \{[^}]*font-size: clamp\(10px, calc\(var\(--gn-sidebar-tree-font-size, var\(--gn-font-size-sm, 12px\)\) - 1px\), 16px\);/s);
+ expect(css).toMatch(/\.gn-v2-tree-title\.is-redis-db \.gn-v2-tree-label \{[^}]*display: inline-flex;[^}]*gap: 6px;/s);
+ expect(css).toMatch(/\.gn-v2-redis-db-alias \{[^}]*color: var\(--gn-fg-5\);[^}]*opacity: 0\.78;/s);
expect(css).toMatch(/\.gn-v2-connection-rail \{[^}]*width: calc\(38px \* var\(--gn-ui-scale, 1\)\);[^}]*flex: 0 0 calc\(38px \* var\(--gn-ui-scale, 1\)\);/s);
expect(css).toMatch(/\.gn-v2-rail-item,\s*body\[data-ui-version="v2"\] \.gn-v2-rail-tool \{[^}]*width: calc\(36px \* var\(--gn-ui-scale, 1\)\);[^}]*height: calc\(38px \* var\(--gn-ui-scale, 1\)\);[^}]*font-size: var\(--gn-font-size-sm, 12px\);/s);
expect(css).toMatch(/\.gn-v2-rail-tool \{[^}]*height: calc\(32px \* var\(--gn-ui-scale, 1\)\);/s);
@@ -2543,6 +2545,25 @@ describe('Sidebar locate toolbar', () => {
expect(source).toContain("title: buildConnectionRootRedisMonitorTabTitle(`db${redisDB}`)");
});
+ it('keeps redis db key counts in v2 meta and renders aliases separately from the db title', () => {
+ const loaderSource = readSourceFile('./sidebar/useSidebarTreeLoaders.tsx');
+ const redisLoadStart = loaderSource.indexOf("if (conn.config.type === 'redis') {");
+ const redisLoadEnd = loaderSource.indexOf('const res = await DBGetDatabases', redisLoadStart);
+ expect(redisLoadStart).toBeGreaterThanOrEqual(0);
+ expect(redisLoadEnd).toBeGreaterThan(redisLoadStart);
+ const redisLoadSource = loaderSource.slice(redisLoadStart, redisLoadEnd);
+
+ expect(redisLoadSource).toContain('const alias = getRedisDbAlias(redisDbAliases, conn.id, db.index);');
+ expect(redisLoadSource).toContain('title: buildRedisDbNodeLabel(');
+ expect(redisLoadSource).toContain('dataRef: { ...conn, redisDB: db.index, redisKeyCount: keyCount, redisDbAlias: alias }');
+ expect(redisLoadSource).not.toContain("keyCount > 0 ? ` (${keyCount})` : ''");
+
+ const titleSource = readSourceFile('./sidebar/SidebarTreeTitle.tsx');
+ expect(titleSource).toContain("node.type === 'redis-db' ? 'is-redis-db' : ''");
+ expect(titleSource).toContain("const redisDbAlias = node.type === 'redis-db'");
+ expect(titleSource).toContain('className="gn-v2-redis-db-alias"');
+ });
+
it('localizes sidebar JVM probe and resource failure prompts', () => {
const source = readSidebarSource();
diff --git a/frontend/src/components/sidebar/SidebarTreeTitle.tsx b/frontend/src/components/sidebar/SidebarTreeTitle.tsx
index b4cf3a36..516d4f31 100644
--- a/frontend/src/components/sidebar/SidebarTreeTitle.tsx
+++ b/frontend/src/components/sidebar/SidebarTreeTitle.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { StarFilled, StarOutlined } from '@ant-design/icons';
import { t } from '../../i18n';
import { SIDEBAR_SQL_EDITOR_DRAG_MIME, encodeSidebarSqlEditorDragPayload } from '../../utils/sidebarSqlDrag';
+import { sanitizeRedisDbAlias } from '../../utils/redisDbAlias';
import { resolveSidebarObjectDragText } from '../sidebarCoreUtils';
import { resolveV2ObjectGroupTitle } from './sidebarHelpers';
@@ -52,6 +53,11 @@ export const renderSidebarV2TreeTitle = ({
return rawTitle;
})();
const metaText = getV2TreeMetaText(node);
+ const redisDbAlias = node.type === 'redis-db'
+ ? sanitizeRedisDbAlias(node?.dataRef?.redisDbAlias)
+ : '';
+ const redisDbIndex = Number(node?.dataRef?.redisDB);
+ const redisDbBaseTitle = Number.isFinite(redisDbIndex) ? `db${redisDbIndex}` : displayTitle;
const isMono = node.type === 'table'
|| node.type === 'view'
|| node.type === 'materialized-view'
@@ -66,6 +72,7 @@ export const renderSidebarV2TreeTitle = ({
'gn-v2-tree-title',
isMono ? 'is-mono' : '',
node.type === 'object-group' ? 'is-group' : '',
+ node.type === 'redis-db' ? 'is-redis-db' : '',
node.type === 'table' && node?.dataRef?.pinnedSidebarTable ? 'is-pinned-table' : '',
].filter(Boolean).join(' ');
const tablePinAction = node.type === 'table' ? (
@@ -145,7 +152,14 @@ export const renderSidebarV2TreeTitle = ({
} : undefined}
>
{statusBadge}
- {displayTitle}
+
+ {redisDbAlias ? (
+ <>
+ {redisDbBaseTitle}
+ {redisDbAlias}
+ >
+ ) : displayTitle}
+
{metaText && {metaText}}
{tablePinAction}
diff --git a/frontend/src/components/sidebar/sidebarLegacyNodeMenu.tsx b/frontend/src/components/sidebar/sidebarLegacyNodeMenu.tsx
index 6129962d..0d0ab1ab 100644
--- a/frontend/src/components/sidebar/sidebarLegacyNodeMenu.tsx
+++ b/frontend/src/components/sidebar/sidebarLegacyNodeMenu.tsx
@@ -38,17 +38,25 @@ import {
} from '../../utils/redisDbAlias';
import { supportsTableTruncateAction } from '../tableDataDangerActions';
-const updateTreeNodeTitle = (
+const updateRedisDbNodeAlias = (
nodes: any[],
targetKey: string,
title: string,
+ alias: string,
): any[] =>
nodes.map((node) => {
if (node.key === targetKey) {
- return { ...node, title };
+ return {
+ ...node,
+ title,
+ dataRef: {
+ ...(node.dataRef || {}),
+ redisDbAlias: alias,
+ },
+ };
}
if (Array.isArray(node.children)) {
- return { ...node, children: updateTreeNodeTitle(node.children, targetKey, title) };
+ return { ...node, children: updateRedisDbNodeAlias(node.children, targetKey, title, alias) };
}
return node;
});
@@ -57,7 +65,7 @@ const openRedisDbAliasModal = (
node: any,
context: SidebarLegacyNodeMenuContext,
): void => {
- const { id, redisDB, redisKeyCount } = node.dataRef;
+ const { id, redisDB } = node.dataRef;
const { treeDataRef, setTreeData } = context;
const currentAlias = getRedisDbAlias(
useStore.getState().appearance.redisDbAliases,
@@ -91,10 +99,8 @@ const openRedisDbAliasModal = (
id,
redisDB,
);
- const keyCount = Number(redisKeyCount);
- const suffix = Number.isFinite(keyCount) && keyCount > 0 ? ` (${keyCount})` : '';
- const nextTitle = buildRedisDbNodeLabel(redisDB, nextAlias, suffix);
- const nextTree = updateTreeNodeTitle(treeDataRef.current, node.key, nextTitle);
+ const nextTitle = buildRedisDbNodeLabel(redisDB, nextAlias);
+ const nextTree = updateRedisDbNodeAlias(treeDataRef.current, node.key, nextTitle, nextAlias);
treeDataRef.current = nextTree;
setTreeData(nextTree);
}
diff --git a/frontend/src/components/sidebar/useSidebarTreeLoaders.tsx b/frontend/src/components/sidebar/useSidebarTreeLoaders.tsx
index 931d2e13..fb598766 100644
--- a/frontend/src/components/sidebar/useSidebarTreeLoaders.tsx
+++ b/frontend/src/components/sidebar/useSidebarTreeLoaders.tsx
@@ -314,16 +314,16 @@ export const useSidebarTreeLoaders = ({
const redisDbAliases = useStore.getState().appearance.redisDbAliases;
let dbs = redisRows.map((db: any) => {
const keyCount = Number(db.keys) > 0 ? Number(db.keys) : 0;
+ const alias = getRedisDbAlias(redisDbAliases, conn.id, db.index);
return {
title: buildRedisDbNodeLabel(
db.index,
- getRedisDbAlias(redisDbAliases, conn.id, db.index),
- keyCount > 0 ? ` (${keyCount})` : '',
+ alias,
),
key: `${conn.id}-db${db.index}`,
icon: ,
type: 'redis-db' as const,
- dataRef: { ...conn, redisDB: db.index, redisKeyCount: keyCount },
+ dataRef: { ...conn, redisDB: db.index, redisKeyCount: keyCount, redisDbAlias: alias },
isLeaf: true,
dbIndex: db.index,
};
diff --git a/frontend/src/utils/redisDbAlias.test.ts b/frontend/src/utils/redisDbAlias.test.ts
index a8856db6..fd455699 100644
--- a/frontend/src/utils/redisDbAlias.test.ts
+++ b/frontend/src/utils/redisDbAlias.test.ts
@@ -68,13 +68,12 @@ describe('redisDbAlias helpers', () => {
});
it('builds the sidebar label with and without an alias', () => {
- expect(buildRedisDbNodeLabel(0, 'cache')).toBe('db0 (cache)');
+ expect(buildRedisDbNodeLabel(0, 'cache')).toBe('db0 cache');
expect(buildRedisDbNodeLabel(3, '')).toBe('db3');
expect(buildRedisDbNodeLabel(0, ' ')).toBe('db0');
});
- it('appends the key-count suffix after the alias', () => {
- expect(buildRedisDbNodeLabel(0, 'cache', ' (12)')).toBe('db0 (cache) (12)');
- expect(buildRedisDbNodeLabel(0, '', ' (12)')).toBe('db0 (12)');
+ it('does not append key counts to the sidebar label', () => {
+ expect(buildRedisDbNodeLabel(0, '12')).toBe('db0 12');
});
});
diff --git a/frontend/src/utils/redisDbAlias.ts b/frontend/src/utils/redisDbAlias.ts
index 69e84446..f8d13823 100644
--- a/frontend/src/utils/redisDbAlias.ts
+++ b/frontend/src/utils/redisDbAlias.ts
@@ -5,7 +5,7 @@
* user works with several connections that each use those indices for a
* different purpose, the numbers are indistinguishable in the sidebar. An alias
* map lets the user label, for example, `db0` as `cache` and have the sidebar
- * render `db0 (cache)`.
+ * render `db0 cache`.
*
* The map is purely a client-side display preference and is keyed by
* connection id so aliases stay independent across connections. The underlying
@@ -128,17 +128,14 @@ export const setRedisDbAlias = (
/**
* Build the sidebar label for a Redis DB node. Returns `dbN` when there is no
- * alias, and `dbN (alias)` when one is set. `suffix` carries the existing
- * key-count fragment (e.g. ` (12)`) and is always appended last so the alias
- * stays adjacent to the index.
+ * alias, and `dbN alias` when one is set. Key counts are intentionally not
+ * included here: V2 renders them via the node meta slot to avoid duplicates.
*/
export const buildRedisDbNodeLabel = (
dbIndex: number,
alias: string,
- suffix = '',
): string => {
const base = `db${dbIndex}`;
const sanitizedAlias = sanitizeRedisDbAlias(alias);
- const labelled = sanitizedAlias ? `${base} (${sanitizedAlias})` : base;
- return `${labelled}${suffix}`;
+ return sanitizedAlias ? `${base} ${sanitizedAlias}` : base;
};
diff --git a/frontend/src/v2-theme.css b/frontend/src/v2-theme.css
index 9374a490..d91522fd 100644
--- a/frontend/src/v2-theme.css
+++ b/frontend/src/v2-theme.css
@@ -2841,6 +2841,20 @@ body[data-ui-version="v2"] .gn-v2-tree-label {
font-weight: 400 !important;
}
+body[data-ui-version="v2"] .gn-v2-tree-title.is-redis-db .gn-v2-tree-label {
+ display: inline-flex;
+ align-items: baseline;
+ gap: 6px;
+}
+
+body[data-ui-version="v2"] .gn-v2-redis-db-alias {
+ color: var(--gn-fg-5);
+ font-family: var(--gn-font-sans);
+ font-size: clamp(10px, calc(var(--gn-sidebar-tree-font-size, var(--gn-font-size-sm, 12px)) - 1px), 16px);
+ font-weight: 400 !important;
+ opacity: 0.78;
+}
+
body[data-ui-version="v2"] .gn-v2-tree-connection-copy {
min-width: max-content;
flex: 0 0 auto;