diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index d04a49276..97dea0d17 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -4105,6 +4105,7 @@ onUnmounted(cleanupPreviewEditor); {{ t("settings.sidebarObjectInfoModeCommentAligned") }} + {{ t("settings.sidebarObjectInfoModeCommentRight") }} {{ t("settings.sidebarObjectInfoModeCommentInline") }} {{ t("settings.sidebarObjectInfoModeSize") }} {{ t("settings.sidebarObjectInfoModeHidden") }} diff --git a/apps/desktop/src/components/sidebar/TreeItem.vue b/apps/desktop/src/components/sidebar/TreeItem.vue index fbe6788ab..61aa28509 100644 --- a/apps/desktop/src/components/sidebar/TreeItem.vue +++ b/apps/desktop/src/components/sidebar/TreeItem.vue @@ -44,7 +44,7 @@ import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import LightTooltip from "@/components/ui/LightTooltip.vue"; import type { ColumnInfo, ConnectionConfig, DatabaseType, TreeNode, TreeNodeType } from "@/types/database"; -import { canTreeNodeShowExpander, sidebarTreeNodeComment, treeItemPaddingLeft, treeLabelWidthClass, usesFullWidthTreeLabel } from "@/lib/sidebar/sidebarTreeItemLayout"; +import { canTreeNodeShowExpander, sidebarTreeNodeComment, trailingCommentAvailableWidth, trailingCommentGapPx, treeItemPaddingLeft, treeLabelWidthClass, usesFullWidthTreeLabel } from "@/lib/sidebar/sidebarTreeItemLayout"; import { clearActiveTableReferencePayload, createTableReferencePayload, createTableReferenceDropEvent, setActiveTableReferencePayload, type QueryEditorTableReferencePayload } from "@/lib/editor/queryEditorTableDrop"; import { formatSidebarObjectStorage } from "@/lib/sidebar/sidebarDatabaseStorage"; import { dataTabOpenModeFromTreeClick } from "@/lib/sidebar/dataTabOpenPolicy"; @@ -69,12 +69,22 @@ const labelRef = ref(); const rowRef = ref(); +const trailingCommentLayoutRef = ref(); + +const trailingCommentLeadingRef = ref(); + +const trailingCommentMaxWidth = ref(0); + const labelOverflowing = ref(false); let labelResizeObserver: ResizeObserver | null = null; +let trailingCommentResizeObserver: ResizeObserver | null = null; + let labelMeasureFrame = 0; +let trailingCommentMeasureFrame = 0; + function cancelLabelOverflowMeasure() { if (!labelMeasureFrame) return; window.cancelAnimationFrame(labelMeasureFrame); @@ -541,10 +551,61 @@ const isNodeDefaultDatabase = computed( ); const trailingComment = computed(() => { - if (settingsStore.editorSettings.sidebarObjectInfoMode !== "comment-inline" && settingsStore.editorSettings.sidebarObjectInfoMode !== "comment-aligned") return null; + if (!settingsStore.editorSettings.sidebarObjectInfoMode.startsWith("comment-")) return null; return sidebarTreeNodeComment(activeNode.value); }); +function isRightAlignedComment(): boolean { + return settingsStore.editorSettings.sidebarObjectInfoMode === "comment-right" && !!trailingComment.value; +} + +function cancelTrailingCommentMeasure() { + if (!trailingCommentMeasureFrame) return; + window.cancelAnimationFrame(trailingCommentMeasureFrame); + trailingCommentMeasureFrame = 0; +} + +function measureTrailingCommentLayout() { + const container = trailingCommentLayoutRef.value; + const leading = trailingCommentLeadingRef.value; + if (!isRightAlignedComment() || !container || !leading) { + trailingCommentMaxWidth.value = 0; + return; + } + trailingCommentMaxWidth.value = trailingCommentAvailableWidth(container.clientWidth, leading.scrollWidth); +} + +function scheduleTrailingCommentMeasure() { + if (typeof window === "undefined") { + measureTrailingCommentLayout(); + return; + } + cancelTrailingCommentMeasure(); + trailingCommentMeasureFrame = window.requestAnimationFrame(() => { + trailingCommentMeasureFrame = 0; + measureTrailingCommentLayout(); + }); +} + +function refreshTrailingCommentMeasurement() { + trailingCommentResizeObserver?.disconnect(); + trailingCommentResizeObserver = null; + + const container = trailingCommentLayoutRef.value; + const leading = trailingCommentLeadingRef.value; + if (!isRightAlignedComment() || !container || !leading) { + trailingCommentMaxWidth.value = 0; + return; + } + + scheduleTrailingCommentMeasure(); + if (typeof ResizeObserver !== "undefined") { + trailingCommentResizeObserver = new ResizeObserver(scheduleTrailingCommentMeasure); + trailingCommentResizeObserver.observe(container); + trailingCommentResizeObserver.observe(leading); + } +} + function formattedObjectStorage(): string { if (settingsStore.editorSettings.sidebarObjectInfoMode !== "size" || (activeNode.value.type !== "database" && activeNode.value.type !== "table" && activeNode.value.type !== "materialized_view")) return ""; return formatSidebarObjectStorage(activeNode.value.sizeBytes); @@ -562,6 +623,8 @@ const rowWidthClass = computed(() => (usesFullWidthLabel.value ? "w-max min-w-fu const labelWidthClass = computed(() => treeLabelWidthClass({ fullWidth: usesFullWidthLabel.value, hasTrailingComment: hasTrailingMetadata() })); +watch(() => [isRightAlignedComment(), visibleLabel(activeNode.value), trailingComment.value, trailingCommentLayoutRef.value, trailingCommentLeadingRef.value], refreshTrailingCommentMeasurement, { flush: "post", immediate: true }); + const paddingLeft = computed(() => treeItemPaddingLeft(props.depth)); const tableSearchParentId = computed(() => activeNode.value.tableSearchParentId || ""); @@ -868,6 +931,8 @@ watch( onBeforeUnmount(() => { stopPasteHandlerRegistration(); handleMouseLeave(); + trailingCommentResizeObserver?.disconnect(); + cancelTrailingCommentMeasure(); finishTableReferenceDrag(); }); @@ -1003,8 +1068,12 @@ function onKeydown(event: KeyboardEvent) { -
-
+
+
- {{ trailingComment }} + {{ trailingComment }} +
@@ -1081,16 +1158,22 @@ function onKeydown(event: KeyboardEvent) {