diff --git a/package.json b/package.json index 9d61ced..9c3c279 100644 --- a/package.json +++ b/package.json @@ -169,6 +169,12 @@ "type": "string", "default": "", "description": "Your user ID for 'Assign to me'. Defaults to $USER if not set." + }, + "beads.tooltipHoverDelay": { + "type": "number", + "default": 1000, + "minimum": 0, + "description": "Delay in milliseconds before showing bead description tooltip on hover. Set to 0 to disable tooltips." } } } diff --git a/src/backend/types.ts b/src/backend/types.ts index 7a131f3..85ca3bd 100644 --- a/src/backend/types.ts +++ b/src/backend/types.ts @@ -146,6 +146,7 @@ export interface BeadsSummary { // Settings that can be passed to webview export interface WebviewSettings { renderMarkdown: boolean; + tooltipHoverDelay: number; // 0 = disabled } // Messages sent from extension to webview diff --git a/src/providers/BaseViewProvider.ts b/src/providers/BaseViewProvider.ts index b20ce53..e611fa9 100644 --- a/src/providers/BaseViewProvider.ts +++ b/src/providers/BaseViewProvider.ts @@ -95,6 +95,7 @@ export abstract class BaseViewProvider implements vscode.WebviewViewProvider { settings: { renderMarkdown: config.get("renderMarkdown", true), userId, + tooltipHoverDelay: config.get("tooltipHoverDelay", 1000), }, }); diff --git a/src/webview/App.tsx b/src/webview/App.tsx index be14221..836d167 100644 --- a/src/webview/App.tsx +++ b/src/webview/App.tsx @@ -43,7 +43,7 @@ const initialState: AppState = { summary: null, loading: true, error: null, - settings: { renderMarkdown: true, userId: "" }, + settings: { renderMarkdown: true, userId: "", tooltipHoverDelay: 1000 }, }; export function App(): React.ReactElement { @@ -146,6 +146,7 @@ export function App(): React.ReactElement { selectedBeadId={state.selectedBeadId} projects={state.projects} activeProject={state.project} + tooltipHoverDelay={state.settings.tooltipHoverDelay} onSelectProject={(projectId) => vscode.postMessage({ type: "selectProject", projectId }) } diff --git a/src/webview/types.ts b/src/webview/types.ts index 5e92379..d75df3c 100644 --- a/src/webview/types.ts +++ b/src/webview/types.ts @@ -74,6 +74,7 @@ export interface BeadsSummary { export interface WebviewSettings { renderMarkdown: boolean; userId: string; + tooltipHoverDelay: number; // 0 = disabled } // Messages from extension to webview diff --git a/src/webview/views/IssuesView.tsx b/src/webview/views/IssuesView.tsx index 956dc69..e060bcb 100644 --- a/src/webview/views/IssuesView.tsx +++ b/src/webview/views/IssuesView.tsx @@ -64,6 +64,7 @@ interface IssuesViewProps { selectedBeadId: string | null; projects: BeadsProject[]; activeProject: BeadsProject | null; + tooltipHoverDelay: number; // 0 = disabled onSelectProject: (projectId: string) => void; onSelectBead: (beadId: string) => void; onUpdateBead: (beadId: string, updates: Partial) => void; @@ -107,6 +108,7 @@ export function IssuesView({ selectedBeadId, projects, activeProject, + tooltipHoverDelay, onSelectProject, onSelectBead, onUpdateBead: _onUpdateBead, @@ -165,11 +167,15 @@ export function IssuesView({ }, [hoveredRowId, beads]); const handleRowMouseEnter = useCallback((e: React.MouseEvent, beadId: string) => { + // Skip if tooltips are disabled + if (tooltipHoverDelay === 0) return; + if (tooltipTimeoutRef.current) { clearTimeout(tooltipTimeoutRef.current); } const rect = e.currentTarget.getBoundingClientRect(); const tooltipWidth = 300; + const tooltipMaxHeight = 200; const padding = 8; // Position below the row, left-aligned with some offset @@ -181,19 +187,24 @@ export function IssuesView({ left = window.innerWidth - tooltipWidth - padding; } - // If tooltip would go below viewport, show above instead - if (top + 150 > window.innerHeight) { - top = rect.top - 150 - padding; + // Check if tooltip would overflow below viewport + const spaceBelow = window.innerHeight - rect.bottom - padding; + const spaceAbove = rect.top - padding; + + if (spaceBelow < tooltipMaxHeight && spaceAbove > spaceBelow) { + // Position above the row when there's more space above + top = rect.top - tooltipMaxHeight - padding; + // Clamp to viewport top if (top < padding) { - top = rect.bottom + padding; + top = padding; } } tooltipTimeoutRef.current = setTimeout(() => { setHoveredRowId(beadId); setTooltipPosition({ top, left }); - }, 400); - }, []); + }, tooltipHoverDelay); + }, [tooltipHoverDelay]); const handleRowMouseLeave = useCallback(() => { if (tooltipTimeoutRef.current) {