Files
goose/fix_text_selection_functionality.py
T
spencrmartin f9cbf70f70 feat: Rich text input with action and mention pills
- Implement RichChatInput component with visual pill rendering
- Add ActionPill component with blue circle icons for actions
- Add MentionPill component with blue diamond icons for files
- Support for /action and @filename pill creation
- Inline pill display in both input and message history
- Proper text selection, highlighting, and cursor positioning
- Backspace removal of entire pills
- Theme-aware styling for light/dark modes
- Consistent white pill backgrounds with blue accent icons
- Fixed line-breaking issues for natural text flow

Components added:
- RichChatInput.tsx: Main rich text input with pill rendering
- ActionPill.tsx: Blue circle pills for actions
- MentionPill.tsx: Blue diamond pills for file mentions
- MessageContent.tsx: Enhanced message display with pills

Features:
- Visual pills replace [Action] and @filename text
- Click and drag text selection works normally
- Double/triple click for word/all selection
- Pills flow inline with text content
- Proper cursor positioning and blinking
- Backspace removes entire pills as units
2025-10-23 09:51:44 -04:00

137 lines
4.4 KiB
Python

import re
# Read the RichChatInput file
with open('ui/desktop/src/components/RichChatInput.tsx', 'r') as f:
content = f.read()
print("Fixing text selection functionality (highlight, double-click, triple-click)...")
# The issue is that we have a hidden textarea and a visual display layer
# We need to make sure the visual layer doesn't interfere with text selection
# Find the visual display div and update its properties
old_display_div = ''' {/* Visual display with action pills and cursor */}
<div
ref={displayRef}
onClick={handleDisplayClick}
className={`${className} cursor-text relative`}
style={{
...style,
minHeight: `${rows * 1.5}em`,
zIndex: 0,
}}
role="textbox"
aria-multiline="true"
aria-placeholder={placeholder}
>
{renderContent()}
</div>'''
new_display_div = ''' {/* Visual display with action pills and cursor */}
<div
ref={displayRef}
onClick={handleDisplayClick}
onDoubleClick={handleDisplayClick}
className={`${className} cursor-text relative`}
style={{
...style,
minHeight: `${rows * 1.5}em`,
zIndex: 0,
userSelect: 'text', // Allow text selection
WebkitUserSelect: 'text', // Safari support
}}
role="textbox"
aria-multiline="true"
aria-placeholder={placeholder}
>
{renderContent()}
</div>'''
content = content.replace(old_display_div, new_display_div)
# Also need to update the hidden textarea to be more accessible for selection
old_hidden_textarea = ''' className="absolute inset-0 w-full h-full opacity-0 pointer-events-auto resize-none"
style={{
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
opacity: 0,
zIndex: 1,
background: 'transparent',
border: 'none',
outline: 'none',
resize: 'none',
}}'''
new_hidden_textarea = ''' className="absolute inset-0 w-full h-full resize-none"
style={{
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
opacity: 0.01, // Nearly invisible but still selectable
zIndex: 1,
background: 'transparent',
border: 'none',
outline: 'none',
resize: 'none',
color: 'transparent', // Hide text but keep selection
caretColor: 'transparent', // Hide caret (we show our own)
}}'''
content = content.replace(old_hidden_textarea, new_hidden_textarea)
# Update handleDisplayClick to handle selection events better
old_handle_click = ''' const handleDisplayClick = useCallback((e: React.MouseEvent) => {
// Don't focus if clicking on an action pill
if ((e.target as Element).closest('.action-pill-wrapper')) {
return;
}
hiddenTextareaRef.current?.focus();
}, []);'''
new_handle_click = ''' const handleDisplayClick = useCallback((e: React.MouseEvent) => {
// Don't focus if clicking on an action pill
if ((e.target as Element).closest('.action-pill-wrapper')) {
return;
}
// Focus the hidden textarea for text selection
if (hiddenTextareaRef.current) {
hiddenTextareaRef.current.focus();
// For double/triple click, select text appropriately
if (e.detail === 2) {
// Double click - select word (let browser handle this)
setTimeout(() => {
if (hiddenTextareaRef.current) {
hiddenTextareaRef.current.select();
}
}, 0);
} else if (e.detail === 3) {
// Triple click - select all
setTimeout(() => {
if (hiddenTextareaRef.current) {
hiddenTextareaRef.current.select();
}
}, 0);
}
}
}, []);'''
content = content.replace(old_handle_click, new_handle_click)
# Write the modified content back
with open('ui/desktop/src/components/RichChatInput.tsx', 'w') as f:
f.write(content)
print("✅ Fixed text selection functionality:")
print(" - Added userSelect: 'text' to visual display")
print(" - Made hidden textarea slightly visible (opacity: 0.01)")
print(" - Added double-click and triple-click handling")
print(" - Preserved pill functionality while enabling text selection")
print(" - Should now support highlighting, double-click, and triple-click")