Merge pull request #562 from serenez/codex/fix-260-482-overflow-navigation
Fix sidebar and tab overflow navigation
This commit is contained in:
15 files changed
+322
-59
No files matched your search
@@ -106,6 +106,7 @@ const sidebarObjectDisplayHelp = ref<"grouped" | "simple" | null>(null);
|
||||
const editAutoSelectActiveSidebarNode = ref(settingsStore.editorSettings.autoSelectActiveSidebarNode);
|
||||
const editSidebarHiddenTablePrefixes = ref(settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n"));
|
||||
const editSidebarHideTableComments = ref(settingsStore.editorSettings.sidebarHideTableComments);
|
||||
const editSidebarAllowHorizontalScroll = ref(settingsStore.editorSettings.sidebarAllowHorizontalScroll);
|
||||
const redisScanPageSizeOptions = [200, 1000, 5000, 10000];
|
||||
const systemFonts = ref<string[]>([]);
|
||||
const systemFontsLoading = ref(false);
|
||||
@@ -241,6 +242,7 @@ watch(
|
||||
editAutoSelectActiveSidebarNode.value = settingsStore.editorSettings.autoSelectActiveSidebarNode;
|
||||
editSidebarHiddenTablePrefixes.value = settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n");
|
||||
editSidebarHideTableComments.value = settingsStore.editorSettings.sidebarHideTableComments;
|
||||
editSidebarAllowHorizontalScroll.value = settingsStore.editorSettings.sidebarAllowHorizontalScroll;
|
||||
editSnippets.value = settingsStore.editorSettings.snippets.map((s) => ({ ...s }));
|
||||
void loadSystemFontOptions();
|
||||
}
|
||||
@@ -279,6 +281,7 @@ function hasChanges(): boolean {
|
||||
editSidebarObjectDisplay.value !== settingsStore.editorSettings.sidebarObjectDisplay ||
|
||||
editAutoSelectActiveSidebarNode.value !== settingsStore.editorSettings.autoSelectActiveSidebarNode ||
|
||||
editSidebarHideTableComments.value !== settingsStore.editorSettings.sidebarHideTableComments ||
|
||||
editSidebarAllowHorizontalScroll.value !== settingsStore.editorSettings.sidebarAllowHorizontalScroll ||
|
||||
JSON.stringify(normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value)) !==
|
||||
JSON.stringify(settingsStore.editorSettings.sidebarHiddenTablePrefixes) ||
|
||||
JSON.stringify(editSnippets.value) !== JSON.stringify(settingsStore.editorSettings.snippets)
|
||||
@@ -305,6 +308,7 @@ async function persistSettings() {
|
||||
sidebarObjectDisplay: editSidebarObjectDisplay.value,
|
||||
autoSelectActiveSidebarNode: editAutoSelectActiveSidebarNode.value,
|
||||
sidebarHideTableComments: editSidebarHideTableComments.value,
|
||||
sidebarAllowHorizontalScroll: editSidebarAllowHorizontalScroll.value,
|
||||
sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value),
|
||||
snippets: editSnippets.value,
|
||||
});
|
||||
@@ -344,6 +348,7 @@ function resetDefaults() {
|
||||
editSidebarObjectDisplay.value = DEFAULT_EDITOR_SETTINGS.sidebarObjectDisplay;
|
||||
editAutoSelectActiveSidebarNode.value = DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode;
|
||||
editSidebarHideTableComments.value = DEFAULT_EDITOR_SETTINGS.sidebarHideTableComments;
|
||||
editSidebarAllowHorizontalScroll.value = DEFAULT_EDITOR_SETTINGS.sidebarAllowHorizontalScroll;
|
||||
editSidebarHiddenTablePrefixes.value = DEFAULT_EDITOR_SETTINGS.sidebarHiddenTablePrefixes.join("\n");
|
||||
editSnippets.value = DEFAULT_SQL_SNIPPETS.map((s) => ({ ...s }));
|
||||
}
|
||||
@@ -1388,6 +1393,22 @@ watch(
|
||||
</div>
|
||||
<Switch id="sidebar-hide-table-comments" v-model="editSidebarHideTableComments" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<Label for="sidebar-allow-horizontal-scroll">
|
||||
{{ t("settings.sidebarAllowHorizontalScroll") }}
|
||||
</Label>
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<CircleHelp class="h-3.5 w-3.5 cursor-help text-muted-foreground hover:text-foreground" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent class="max-w-[320px] text-xs leading-relaxed" side="top" align="start">
|
||||
{{ t("settings.sidebarAllowHorizontalScrollDescription") }}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Switch id="sidebar-allow-horizontal-scroll" v-model="editSidebarAllowHorizontalScroll" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="sidebar-hidden-table-prefixes">{{ t("settings.sidebarHiddenTablePrefixes") }}</Label>
|
||||
<textarea
|
||||
|
||||
@@ -2,14 +2,31 @@
|
||||
import { computed, ref, watch, nextTick } from "vue";
|
||||
import type { CSSProperties } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { X, Pin, ChevronDown, Table2, Code2, TableProperties, PencilRuler, Package, Check } from "lucide-vue-next";
|
||||
import {
|
||||
X,
|
||||
Pin,
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Table2,
|
||||
Code2,
|
||||
TableProperties,
|
||||
PencilRuler,
|
||||
Package,
|
||||
Check,
|
||||
} from "lucide-vue-next";
|
||||
import CustomContextMenu, { type ContextMenuItem } from "@/components/ui/CustomContextMenu.vue";
|
||||
import LightDropdown from "@/components/ui/LightDropdown.vue";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useTabScroll } from "@/composables/useTabScroll";
|
||||
import { connectionColor, tabDisplayTitle, tabTooltipLines } from "@/lib/tabPresentation";
|
||||
import {
|
||||
connectionColor,
|
||||
shouldShowTabOverflowControls,
|
||||
tabDisplayTitle,
|
||||
tabTooltipLines,
|
||||
} from "@/lib/tabPresentation";
|
||||
import { hexToRgba } from "@/lib/color";
|
||||
import type { QueryTab } from "@/types/database";
|
||||
|
||||
@@ -70,7 +87,8 @@ function getTabMenuItems(tab: QueryTab): ContextMenuItem[] {
|
||||
}
|
||||
|
||||
const tabsContainerRef = ref<HTMLElement | null>(null);
|
||||
const { canScrollLeft, canScrollRight, updateScrollButtons } = useTabScroll(tabsContainerRef);
|
||||
const { hasTabOverflow, canScrollLeft, canScrollRight, updateScrollButtons, scrollTabs, onTabsWheel } =
|
||||
useTabScroll(tabsContainerRef);
|
||||
const tabScrollBehavior = ref<ScrollBehavior>("smooth");
|
||||
|
||||
watch(
|
||||
@@ -146,21 +164,33 @@ function tabIconClass(tab: QueryTab) {
|
||||
return "text-blue-600 dark:text-blue-400";
|
||||
}
|
||||
|
||||
const dataTabs = computed(() => queryStore.tabs.filter((tab) => tab.mode === "data"));
|
||||
const showPinnedDataTabsMenu = computed(
|
||||
() => dataTabs.value.length > 0 && (canScrollLeft.value || canScrollRight.value),
|
||||
const showTabOverflowControls = computed(() =>
|
||||
shouldShowTabOverflowControls(
|
||||
queryStore.tabs.length,
|
||||
hasTabOverflow.value,
|
||||
canScrollLeft.value,
|
||||
canScrollRight.value,
|
||||
),
|
||||
);
|
||||
const dataTabMenuItems = computed(() =>
|
||||
dataTabs.value.map((tab) => ({
|
||||
|
||||
const openTabMenuItems = computed(() =>
|
||||
queryStore.tabs.map((tab) => ({
|
||||
value: tab.id,
|
||||
label: tabDisplayTitle(tab, t),
|
||||
title: tabDisplayTitle(tab, t),
|
||||
icon: Table2,
|
||||
iconClass: "text-emerald-600 dark:text-emerald-400",
|
||||
icon: tabMenuIcon(tab),
|
||||
iconClass: tabIconClass(tab),
|
||||
})),
|
||||
);
|
||||
|
||||
function activateDataTab(tabId: string) {
|
||||
function tabMenuIcon(tab: QueryTab) {
|
||||
if (tab.mode === "data") return Table2;
|
||||
if (tab.mode === "objects") return TableProperties;
|
||||
if (tab.mode === "structure") return PencilRuler;
|
||||
return Code2;
|
||||
}
|
||||
|
||||
function activateTab(tabId: string) {
|
||||
tabScrollBehavior.value = "auto";
|
||||
queryStore.activeTabId = tabId;
|
||||
emit("close-driver-store");
|
||||
@@ -170,17 +200,16 @@ const tabsContainerStyle = computed<CSSProperties>(() => ({
|
||||
msOverflowStyle: "none",
|
||||
scrollbarWidth: "none",
|
||||
WebkitOverflowScrolling: "touch",
|
||||
paddingRight: showPinnedDataTabsMenu.value && settingsStore.editorSettings.appLayout !== "classic" ? "28px" : "0px",
|
||||
}));
|
||||
|
||||
const tabTailDragRegionClass = computed(() =>
|
||||
showPinnedDataTabsMenu.value ? "w-0 flex-none self-stretch" : "min-w-8 flex-1 self-stretch",
|
||||
showTabOverflowControls.value ? "w-0 flex-none self-stretch" : "min-w-8 flex-1 self-stretch",
|
||||
);
|
||||
|
||||
const dataTabsMenuContainerClass = computed(() =>
|
||||
const tabOverflowControlClass = computed(() =>
|
||||
settingsStore.editorSettings.appLayout === "classic"
|
||||
? "relative z-30 flex shrink-0 items-stretch"
|
||||
: "absolute inset-y-0 -right-2 z-30 flex items-stretch",
|
||||
? "h-full w-8 border-r border-border/80 dark:border-border/45 bg-background/80 text-foreground/75 hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-40"
|
||||
: "h-7 w-7 rounded-md border border-border/60 bg-background text-foreground/70 hover:border-border hover:text-foreground",
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -194,17 +223,25 @@ const dataTabsMenuContainerClass = computed(() =>
|
||||
: 'h-10 items-center bg-background px-2'
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-if="canScrollLeft"
|
||||
class="pointer-events-none absolute left-0 z-10 h-full w-6 bg-linear-to-r from-background from-40% to-transparent"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button
|
||||
v-if="showTabOverflowControls"
|
||||
type="button"
|
||||
class="z-30 shrink-0 inline-flex items-center justify-center"
|
||||
:class="[tabOverflowControlClass, canScrollLeft ? '' : 'opacity-40']"
|
||||
:aria-label="t('tabs.scrollLeft')"
|
||||
:title="t('tabs.scrollLeft')"
|
||||
:disabled="!canScrollLeft"
|
||||
@click="scrollTabs('left')"
|
||||
>
|
||||
<ChevronLeft class="h-4 w-4 stroke-[2.5]" />
|
||||
</button>
|
||||
<div
|
||||
ref="tabsContainerRef"
|
||||
class="flex-1 flex items-center overflow-x-auto min-w-0"
|
||||
:class="settingsStore.editorSettings.appLayout === 'classic' ? '' : 'gap-1.5'"
|
||||
:style="tabsContainerStyle"
|
||||
@scroll="updateScrollButtons"
|
||||
@wheel="onTabsWheel"
|
||||
>
|
||||
<CustomContextMenu
|
||||
v-for="tab in queryStore.tabs"
|
||||
@@ -311,29 +348,37 @@ const dataTabsMenuContainerClass = computed(() =>
|
||||
</div>
|
||||
<div :class="tabTailDragRegionClass" data-tauri-drag-region />
|
||||
</div>
|
||||
<div
|
||||
v-if="canScrollRight"
|
||||
class="pointer-events-none absolute right-0 z-20 h-full w-6 bg-linear-to-l from-background from-40% to-transparent"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div v-if="showPinnedDataTabsMenu" :class="dataTabsMenuContainerClass">
|
||||
<div v-if="showTabOverflowControls" class="relative z-30 flex shrink-0 items-center">
|
||||
<button
|
||||
v-if="showTabOverflowControls"
|
||||
type="button"
|
||||
class="inline-flex shrink-0 items-center justify-center"
|
||||
:class="[tabOverflowControlClass, canScrollRight ? '' : 'opacity-40']"
|
||||
:aria-label="t('tabs.scrollRight')"
|
||||
:title="t('tabs.scrollRight')"
|
||||
:disabled="!canScrollRight"
|
||||
@click="scrollTabs('right')"
|
||||
>
|
||||
<ChevronRight class="h-4 w-4 stroke-[2.5]" />
|
||||
</button>
|
||||
<LightDropdown
|
||||
:model-value="queryStore.activeTabId || ''"
|
||||
:items="dataTabMenuItems"
|
||||
:aria-label="t('tabs.openDataTabs')"
|
||||
:items="openTabMenuItems"
|
||||
:aria-label="t('tabs.openTabs')"
|
||||
:trigger-title="t('tabs.openTabs')"
|
||||
:trigger-icon="ChevronDown"
|
||||
trigger-class="h-full w-7 rounded-none text-foreground/70 hover:text-foreground inline-flex items-center justify-center bg-background"
|
||||
:trigger-class="['inline-flex shrink-0 items-center justify-center', tabOverflowControlClass].join(' ')"
|
||||
trigger-icon-class="h-4 w-4"
|
||||
item-icon-class="w-3.5 h-3.5 mr-2"
|
||||
item-class="max-w-full"
|
||||
content-class="w-auto min-w-36 max-w-60"
|
||||
content-class="w-auto min-w-48 max-w-72"
|
||||
:show-trigger-label="false"
|
||||
:show-chevron="false"
|
||||
:highlight-selected="false"
|
||||
:match-trigger-width="false"
|
||||
check-position="none"
|
||||
align="end"
|
||||
@update:model-value="activateDataTab"
|
||||
@update:model-value="activateTab"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -163,6 +163,11 @@ const filteredNodes = computed(() => {
|
||||
const flatNodes = computed<FlatTreeNode[]>(() => flattenTree(filteredNodes.value));
|
||||
const useVirtualTree = computed(() => shouldVirtualizeFlatTree(flatNodes.value.length));
|
||||
const activeTab = computed(() => queryStore.tabs.find((tab) => tab.id === queryStore.activeTabId));
|
||||
const sidebarTreeOverflowClass = computed(() =>
|
||||
settingsStore.editorSettings.sidebarAllowHorizontalScroll
|
||||
? "overflow-x-auto sidebar-tree-horizontal-scroll"
|
||||
: "overflow-x-hidden",
|
||||
);
|
||||
|
||||
const pendingRenameGroupId = ref<string | null>(null);
|
||||
const highlightedNodeId = ref<string | null>(null);
|
||||
@@ -492,7 +497,8 @@ defineExpose({ focusSearch });
|
||||
<RecycleScroller
|
||||
v-if="flatNodes.length > 0 && useVirtualTree"
|
||||
ref="treeScrollerRef"
|
||||
class="sidebar-tree connection-tree-scroller min-h-0 flex-1 overflow-y-auto overflow-x-auto"
|
||||
class="sidebar-tree connection-tree-scroller min-h-0 flex-1 overflow-y-auto"
|
||||
:class="sidebarTreeOverflowClass"
|
||||
:items="flatNodes"
|
||||
:item-size="SIDEBAR_TREE_ROW_HEIGHT"
|
||||
:buffer="SIDEBAR_TREE_SCROLL_BUFFER"
|
||||
@@ -518,7 +524,8 @@ defineExpose({ focusSearch });
|
||||
<div
|
||||
v-else-if="flatNodes.length > 0"
|
||||
ref="plainTreeScrollerRef"
|
||||
class="sidebar-tree min-h-0 flex-1 overflow-y-auto overflow-x-auto"
|
||||
class="sidebar-tree min-h-0 flex-1 overflow-y-auto"
|
||||
:class="sidebarTreeOverflowClass"
|
||||
>
|
||||
<TreeItem
|
||||
v-for="item in flatNodes"
|
||||
@@ -546,6 +553,11 @@ defineExpose({ focusSearch });
|
||||
}
|
||||
|
||||
.connection-tree-scroller :deep(.vue-recycle-scroller__item-view) {
|
||||
contain: layout style paint;
|
||||
min-width: 100%;
|
||||
contain: style;
|
||||
}
|
||||
|
||||
.connection-tree-scroller.sidebar-tree-horizontal-scroll :deep(.vue-recycle-scroller__item-view) {
|
||||
width: max-content;
|
||||
}
|
||||
</style>
|
||||
@@ -56,7 +56,7 @@ import type { DatabaseType, TreeNode, TreeNodeType } from "@/types/database";
|
||||
import * as api from "@/lib/api";
|
||||
import { uuid } from "@/lib/utils";
|
||||
import { resolveDefaultDatabase } from "@/lib/defaultDatabase";
|
||||
import { canTreeNodeShowExpander, treeItemPaddingLeft } from "@/lib/sidebarTreeItemLayout";
|
||||
import { canTreeNodeShowExpander, treeItemPaddingLeft, usesFullWidthTreeLabel } from "@/lib/sidebarTreeItemLayout";
|
||||
import { buildTableSelectSql } from "@/lib/tableSelectSql";
|
||||
import {
|
||||
clearActiveTableReferencePayload,
|
||||
@@ -157,6 +157,14 @@ const emit = defineEmits<{
|
||||
"search-toggle": [node: TreeNode];
|
||||
}>();
|
||||
|
||||
const usesFullWidthLabel = computed(() =>
|
||||
usesFullWidthTreeLabel(props.node.type, settingsStore.editorSettings.sidebarAllowHorizontalScroll),
|
||||
);
|
||||
const rowWidthClass = computed(() => (usesFullWidthLabel.value ? "w-max min-w-full" : "w-full min-w-0"));
|
||||
const labelWidthClass = computed(() =>
|
||||
usesFullWidthLabel.value ? "shrink-0 whitespace-nowrap" : "min-w-0 flex-1 truncate",
|
||||
);
|
||||
|
||||
function currentDatabaseType(): DatabaseType | undefined {
|
||||
return props.node.connectionId ? connectionStore.getConfig(props.node.connectionId)?.db_type : undefined;
|
||||
}
|
||||
@@ -1652,9 +1660,13 @@ const isActiveConnectionScope = computed(
|
||||
const isSelected = computed(() => connectionStore.selectedTreeNodeId === props.node.id);
|
||||
const rowStyle = computed(() => {
|
||||
const color = connectionColor.value;
|
||||
const backgroundColor = hexToRgba(color, isActiveConnectionScope.value ? 0.14 : 0.08);
|
||||
return {
|
||||
paddingLeft: paddingLeft.value,
|
||||
backgroundColor: hexToRgba(color, isActiveConnectionScope.value ? 0.14 : 0.08),
|
||||
"--tree-connection-row-bg": backgroundColor,
|
||||
"--tree-connection-row-hover-bg": hexToRgba(color, isActiveConnectionScope.value ? 0.18 : 0.12),
|
||||
"--tree-connection-active-bg": hexToRgba(color, 0.18),
|
||||
"--tree-connection-active-focus-bg": hexToRgba(color, 0.22),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -2253,16 +2265,21 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
<div @contextmenu="onContextMenu">
|
||||
<div
|
||||
ref="rowRef"
|
||||
class="group flex min-w-0 items-center gap-1.5 py-1 px-2 cursor-pointer hover:bg-accent transition-colors relative outline-none"
|
||||
style="contain: layout style paint"
|
||||
:class="{
|
||||
'ring-1 ring-primary/50 bg-primary/5': showDropInside,
|
||||
'opacity-50': isDragging,
|
||||
'rounded-none': connectionColor && !isSelected,
|
||||
'rounded-sm': !connectionColor && !isSelected,
|
||||
'tree-item-active rounded-md': isSelected,
|
||||
'tree-item-highlight': highlighted,
|
||||
}"
|
||||
class="group flex items-center gap-1.5 py-1 px-2 cursor-pointer hover:bg-accent transition-colors relative outline-none"
|
||||
style="contain: layout style"
|
||||
:class="[
|
||||
rowWidthClass,
|
||||
{
|
||||
'ring-1 ring-primary/50 bg-primary/5': showDropInside,
|
||||
'opacity-50': isDragging,
|
||||
'tree-item-connection-tint': connectionColor,
|
||||
'rounded-none': connectionColor && !isSelected,
|
||||
'rounded-sm': !connectionColor && !isSelected,
|
||||
'tree-item-active rounded-none': connectionColor && isSelected,
|
||||
'tree-item-active rounded-md': !connectionColor && isSelected,
|
||||
'tree-item-highlight': highlighted,
|
||||
},
|
||||
]"
|
||||
:tabindex="isSelected ? 0 : -1"
|
||||
:style="rowStyle"
|
||||
@click="onClick"
|
||||
@@ -2316,7 +2333,7 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
@click.stop
|
||||
/>
|
||||
<LightTooltip v-else :text="displayLabel(node)" :disabled="isTooltipDisabled" side="right" :side-offset="8">
|
||||
<span ref="labelRef" class="min-w-0 flex-1 truncate">{{ visibleLabel(node) }}</span>
|
||||
<span ref="labelRef" :class="labelWidthClass">{{ visibleLabel(node) }}</span>
|
||||
</LightTooltip>
|
||||
<span
|
||||
v-if="
|
||||
@@ -2659,20 +2676,58 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.tree-item-connection-tint {
|
||||
isolation: isolate;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.tree-item-connection-tint::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0 -9999px;
|
||||
z-index: 0;
|
||||
background-color: var(--tree-connection-row-bg);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tree-item-connection-tint > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tree-item-connection-tint:hover,
|
||||
.tree-item-connection-tint.tree-item-active,
|
||||
.tree-item-connection-tint.tree-item-active:focus {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.tree-item-connection-tint:hover::before {
|
||||
background-color: var(--tree-connection-row-hover-bg, var(--tree-connection-row-bg));
|
||||
}
|
||||
|
||||
.tree-item-connection-tint.tree-item-active::before {
|
||||
background-color: var(--tree-connection-active-bg, var(--tree-connection-row-bg));
|
||||
}
|
||||
|
||||
.tree-item-connection-tint.tree-item-active:focus::before {
|
||||
background-color: var(--tree-connection-active-focus-bg, var(--tree-connection-active-bg));
|
||||
}
|
||||
|
||||
/* Unfocused: subtle gray */
|
||||
.tree-item-active {
|
||||
background-color: oklch(0.94 0 0) !important;
|
||||
background-color: var(--tree-connection-active-bg, oklch(0.94 0 0)) !important;
|
||||
}
|
||||
:root.dark .tree-item-active {
|
||||
background-color: oklch(0.26 0 0) !important;
|
||||
background-color: var(--tree-connection-active-bg, oklch(0.26 0 0)) !important;
|
||||
}
|
||||
|
||||
/* Focused: soft blue */
|
||||
.tree-item-active:focus {
|
||||
background-color: oklch(0.91 0.03 250) !important;
|
||||
background-color: var(--tree-connection-active-focus-bg, oklch(0.91 0.03 250)) !important;
|
||||
}
|
||||
:root.dark .tree-item-active:focus {
|
||||
background-color: oklch(0.35 0.06 250) !important;
|
||||
background-color: var(--tree-connection-active-focus-bg, oklch(0.35 0.06 250)) !important;
|
||||
}
|
||||
|
||||
/* Locate highlight: instant amber, then fade on removal */
|
||||
|
||||
Reference in new issue
Block a user