UNPKG

@wonderwhy-er/desktop-commander

Version:

MCP server for terminal operations and file editing

107 lines (106 loc) 4.15 kB
export function attachSelectionContext(options) { if (options.isMarkdownEditing) { if (options.previousAbortController) { options.previousAbortController.abort(); } return null; } const contentWrapper = document.querySelector('.panel-content-wrapper'); if (!contentWrapper) { return options.previousAbortController; } const wrapper = contentWrapper; options.previousAbortController?.abort(); const abortController = new AbortController(); let hintEl = null; let lastSelectedText = ''; let hideTimer = null; function positionHint(selection) { if (!hintEl) return; const range = selection.getRangeAt(0); const rect = range.getBoundingClientRect(); const wrapperRect = wrapper.getBoundingClientRect(); let left = rect.left + rect.width / 2 - wrapperRect.left; let top = rect.top - wrapperRect.top + wrapper.scrollTop - 32; const hintWidth = hintEl.offsetWidth || 200; left = Math.max(8, Math.min(left - hintWidth / 2, wrapper.clientWidth - hintWidth - 8)); top = Math.max(4, top); hintEl.style.left = `${left}px`; hintEl.style.top = `${top}px`; } function showHint(selection) { if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; } if (!hintEl) { hintEl = document.createElement('div'); hintEl.className = 'selection-hint'; hintEl.textContent = 'AI can see your selection'; wrapper.appendChild(hintEl); } hintEl.classList.add('visible'); positionHint(selection); } function hideHint() { if (!hintEl) return; hintEl.classList.remove('visible'); hideTimer = setTimeout(() => { hintEl?.remove(); hintEl = null; }, 200); } function getLineInfo(selection) { const anchorRow = selection.anchorNode?.parentElement?.closest('.code-line'); const focusRow = selection.focusNode?.parentElement?.closest('.code-line'); if (anchorRow && focusRow) { const anchorLine = parseInt(anchorRow.dataset.line ?? '', 10); const focusLine = parseInt(focusRow.dataset.line ?? '', 10); if (!isNaN(anchorLine) && !isNaN(focusLine)) { const low = Math.min(anchorLine, focusLine); const high = Math.max(anchorLine, focusLine); return low === high ? `line ${low}` : `lines ${low}${high}`; } } return ''; } document.addEventListener('selectionchange', () => { const selection = document.getSelection(); if (!selection || selection.isCollapsed) { if (lastSelectedText) { lastSelectedText = ''; options.updateContext?.(''); hideHint(); } return; } const text = selection.toString().trim(); if (!text || text === lastSelectedText) { return; } const anchorInContent = wrapper.contains(selection.anchorNode); const focusInContent = wrapper.contains(selection.focusNode); if (!anchorInContent && !focusInContent) { if (lastSelectedText) { lastSelectedText = ''; options.updateContext?.(''); hideHint(); } return; } lastSelectedText = text; const lineInfo = getLineInfo(selection); const locationPart = lineInfo ? ` (${lineInfo})` : ''; const context = `User selected text from file ${options.payload.filePath}${locationPart}:\n\`\`\`\n${text}\n\`\`\``; options.updateContext?.(context); showHint(selection); options.trackUiEvent?.('text_selected', { file_type: options.payload.fileType, file_extension: options.getFileExtensionForAnalytics(options.payload.filePath), char_count: text.length, }); }, { signal: abortController.signal }); return abortController; }