feat(ui): add connection color tinting

This commit is contained in:
t8y2 2026-05-10 09:18:04 +08:00
parent 4cd9432fcb
commit 0b376c234f
4 changed files with 57 additions and 5 deletions

View File

@ -12,7 +12,9 @@ import {
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
import { useQueryStore } from "@/stores/queryStore";
import { useTabScroll } from "@/composables/useTabScroll";
import { tabDisplayTitle, tabTooltipLines } from "@/lib/tabPresentation";
import { connectionColor, tabDisplayTitle, tabTooltipLines } from "@/lib/tabPresentation";
import { hexToRgba } from "@/lib/color";
import type { QueryTab } from "@/types/database";
const { t } = useI18n();
const queryStore = useQueryStore();
@ -41,6 +43,19 @@ watch(
});
},
);
function tabColorStyle(tab: QueryTab) {
const color = connectionColor(tab.connectionId);
const isActive = tab.id === queryStore.activeTabId;
if (!color) {
return isActive ? { boxShadow: "0 1px 0 0 var(--color-background)" } : undefined;
}
return {
backgroundColor: hexToRgba(color, isActive ? 0.16 : 0.07),
boxShadow: isActive ? `inset 0 -2px 0 ${color}` : undefined,
};
}
</script>
<template>
@ -70,9 +85,7 @@ watch(
? 'bg-background text-foreground font-medium'
: 'text-foreground/70 hover:text-foreground/90'
"
:style="
tab.id === queryStore.activeTabId ? { boxShadow: '0 1px 0 0 var(--color-background)' } : undefined
"
:style="tabColorStyle(tab)"
:data-active-tab="tab.id === queryStore.activeTabId"
@click="queryStore.activeTabId = tab.id"
@mousedown.middle.prevent="queryStore.closeTab(tab.id)"

View File

@ -24,6 +24,7 @@ import { useSchemaOptions } from "@/composables/useSchemaOptions";
import { connectionIconType } from "@/lib/connectionPresentation";
import { isDefaultDatabase } from "@/lib/defaultDatabase";
import { connectionDisplayName } from "@/lib/tabPresentation";
import { hexToRgba } from "@/lib/color";
import type { QueryTab, ConnectionConfig } from "@/types/database";
const props = defineProps<{
@ -72,6 +73,14 @@ const activeSchemaOptions = computed(() => {
});
const isActiveDatabaseDefault = computed(() => isDefaultDatabase(props.activeConnection, activeDatabaseValue.value));
const toolbarStyle = computed(() => {
const color = props.activeConnection?.color;
if (!color) return undefined;
return {
backgroundColor: hexToRgba(color, 0.1),
boxShadow: `inset 0 1px 0 ${hexToRgba(color, 0.18)}`,
};
});
function databaseDisplayName(database: string): string {
const connection = props.activeConnection;
@ -83,6 +92,7 @@ function databaseDisplayName(database: string): string {
<template>
<div
class="h-9 shrink-0 border-b bg-background/80 px-3 flex items-center gap-1 text-xs text-muted-foreground relative z-10"
:style="toolbarStyle"
>
<div class="flex items-center gap-0.5">
<Tooltip>

View File

@ -80,6 +80,7 @@ import {
usesFetchFirst,
} from "@/lib/databaseCapabilities";
import { treeNodeRowAction } from "@/lib/treeNodeClick";
import { hexToRgba } from "@/lib/color";
import DangerConfirmDialog from "@/components/editor/DangerConfirmDialog.vue";
import { isTauriRuntime } from "@/lib/tauriRuntime";
import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
@ -1091,6 +1092,16 @@ const connectionColor = computed(() => {
const connectionId = props.node.connectionId;
return connectionId ? connectionStore.getConfig(connectionId)?.color || "" : "";
});
const isActiveConnectionScope = computed(
() => !!props.node.connectionId && connectionStore.activeConnectionId === props.node.connectionId,
);
const rowStyle = computed(() => {
const color = connectionColor.value;
return {
paddingLeft,
backgroundColor: hexToRgba(color, isActiveConnectionScope.value ? 0.14 : 0.08),
};
});
const CHILDREN_PAGE_SIZE = 100;
const displayLimit = ref(CHILDREN_PAGE_SIZE);
@ -1299,7 +1310,7 @@ const isDragging = computed(() => dragState.active && dragState.draggedId === pr
'ring-1 ring-primary/50 bg-primary/5': showDropInside,
'opacity-50': isDragging,
}"
:style="{ paddingLeft }"
:style="rowStyle"
@click="onClick"
@mousedown="isDraggable ? startDrag($event, node.id, node.type) : undefined"
@mousemove="isDropTarget ? updateTarget($event, node.id, node.type) : undefined"

18
src/lib/color.ts Normal file
View File

@ -0,0 +1,18 @@
export function hexToRgba(color: string | undefined, alpha: number): string | undefined {
if (!color) return undefined;
const hex = color.trim().replace(/^#/, "");
const normalized =
hex.length === 3
? hex
.split("")
.map((c) => c + c)
.join("")
: hex;
if (!/^[0-9a-fA-F]{6}$/.test(normalized)) return undefined;
const value = Number.parseInt(normalized, 16);
const r = (value >> 16) & 255;
const g = (value >> 8) & 255;
const b = value & 255;
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}