From b0492434d03dd29b7a0824f2863d96353494c77b Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Fri, 13 Feb 2026 02:26:15 +0000 Subject: [PATCH] feat: syntax highlighting in chat input textarea Real-time markdown syntax coloring while typing using a transparent textarea overlay approach. Highlights: code blocks, inline code, bold, italic, headings, and links. Toggle button (highlighter icon) to enable/disable, persisted in localStorage. --- src/components/ChatInput.tsx | 18 ++++- src/components/HighlightedTextarea.tsx | 97 ++++++++++++++++++++++++++ src/index.css | 75 ++++++++++++++++++++ 3 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 src/components/HighlightedTextarea.tsx diff --git a/src/components/ChatInput.tsx b/src/components/ChatInput.tsx index b0abde5..55cff41 100644 --- a/src/components/ChatInput.tsx +++ b/src/components/ChatInput.tsx @@ -1,6 +1,7 @@ import { useState, useRef, useEffect, useCallback, lazy, Suspense } from 'react'; -import { Send, Square, Paperclip, X, FileText, Eye, EyeOff } from 'lucide-react'; +import { Send, Square, Paperclip, X, FileText, Eye, EyeOff, Highlighter } from 'lucide-react'; import { useT } from '../hooks/useLocale'; +import { HighlightedTextarea } from './HighlightedTextarea'; const ReactMarkdown = lazy(() => import('react-markdown')); const remarkGfm = import('remark-gfm').then(m => m.default); @@ -91,6 +92,7 @@ export function ChatInput({ onSend, onAbort, isGenerating, disabled, sessionKey const [files, setFiles] = useState([]); const [isDragOver, setIsDragOver] = useState(false); const [showPreview, setShowPreview] = useState(() => localStorage.getItem('pinchchat-md-preview') === '1'); + const [highlightEnabled, setHighlightEnabled] = useState(() => localStorage.getItem('pinchchat-syntax-hl') !== '0'); const textareaRef = useRef(null); const fileInputRef = useRef(null); @@ -283,6 +285,15 @@ export function ChatInput({ onSend, onAbort, isGenerating, disabled, sessionKey > {showPreview ? : } + {/* Syntax highlight toggle */} + -