Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7d79fb5
feat(textarea): working context mention
elianiva Sep 20, 2025
cc510f2
feat(textarea): initial working context bar
elianiva Sep 20, 2025
411fe88
feat(textarea): proper context mention bar
elianiva Sep 22, 2025
78ab8c0
Merge branch 'main' of github.com:RooVetGit/Roo-Code into feat/new-te…
elianiva Sep 29, 2025
324463c
feat(textarea): improved mention context bar
elianiva Sep 29, 2025
b363dc4
fix(context-bar): weird hover state
elianiva Sep 29, 2025
5d408af
fix(textarea): autofocus
elianiva Sep 29, 2025
92a04f5
feat(textarea): navigate through history
elianiva Oct 1, 2025
680ab86
refactor(textarea): simplify event listeners
elianiva Oct 1, 2025
19e7f77
feat(textarea): support parsing mentions from paste
elianiva Oct 1, 2025
a93fb5a
feat(textarea): support mentioning url
elianiva Oct 1, 2025
b08c46c
feat(textarea): hide unnecessary information
elianiva Oct 1, 2025
cb8b2c2
fix(textarea): persist shorthand between undo/redo
elianiva Oct 2, 2025
8c48c73
test(textarea): update tests
elianiva Oct 2, 2025
2481a51
fix(textarea): message couldn't be sent
elianiva Oct 2, 2025
e45375e
refactor(textarea): deprecate old textarea
elianiva Oct 2, 2025
d9f92e7
fix(textarea): handle removal of duplicate mentions
elianiva Oct 2, 2025
044b6a8
Merge branch 'main' of github.com:RooVetGit/Roo-Code into feat/new-te…
elianiva Oct 2, 2025
e78bb23
fix(textarea): handle command mention better
elianiva Oct 2, 2025
d281ce5
fix(textarea): handle escaping values
elianiva Oct 2, 2025
659cadb
fix(textarea): handle paragraph newline
elianiva Oct 2, 2025
0823ec3
refactor(textarea): remove old history hooks
elianiva Oct 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
355 changes: 335 additions & 20 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions webview-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"clean": "rimraf ../src/webview-ui ../apps/vscode-nightly/build/webview-ui tsconfig.tsbuildinfo .turbo"
},
"dependencies": {
"@lexical/react": "^0.35.0",
"@radix-ui/react-alert-dialog": "^1.1.6",
"@radix-ui/react-checkbox": "^1.1.5",
"@radix-ui/react-collapsible": "^1.1.3",
Expand Down Expand Up @@ -48,6 +49,7 @@
"i18next-http-backend": "^3.0.2",
"katex": "^0.16.11",
"knuth-shuffle-seeded": "^1.0.6",
"lexical": "^0.35.0",
"lru-cache": "^11.1.0",
"lucide-react": "^0.518.0",
"mermaid": "^11.4.1",
Expand Down
133 changes: 133 additions & 0 deletions webview-ui/src/components/chat/ChatContextBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { useState, useMemo } from "react"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
import { MentionInfo } from "./lexical/LexicalMentionPlugin"

interface ContextItem {
type: "mention" | "image"
icon: string
displayName: string
originalIndex: number
iconAlt: string
iconStyle?: React.CSSProperties
nodeKey?: string // Only for mention items
}

interface ChatContextBarProps {
validMentions: MentionInfo[]
selectedImages: string[]
onRemoveMention: (index: number) => void
onRemoveImage: (index: number) => void
}

export const ChatContextBar = ({
validMentions,
selectedImages,
onRemoveMention,
onRemoveImage,
}: ChatContextBarProps) => {
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null)

const contextItems = useMemo<ContextItem[]>(() => {
const items: ContextItem[] = []

validMentions.forEach((mention, index) => {
let iconAlt = "File"
if (mention.type === "folder") {
iconAlt = "Folder"
} else if (mention.type === "url") {
iconAlt = "URL"
} else if (mention.type === "problems") {
iconAlt = "Problems"
} else if (mention.type === "terminal") {
iconAlt = "Terminal"
} else if (mention.type === "git") {
iconAlt = "Git"
}

items.push({
type: "mention",
icon: mention.icon,
displayName: mention.displayName,
originalIndex: index,
iconAlt,
nodeKey: mention.nodeKey,
})
})

selectedImages.forEach((image, index) => {
items.push({
type: "image",
icon: image,
displayName: `Image #${index + 1}`,
originalIndex: index,
iconAlt: `Image ${index + 1}`,
iconStyle: {
objectFit: "cover",
borderRadius: "2px",
},
})
})

return items
}, [validMentions, selectedImages])

const shouldShowContextBar = contextItems.length > 0

if (!shouldShowContextBar) {
return null
}

const handleRemove = (item: ContextItem) => {
if (item.type === "mention") {
onRemoveMention(item.originalIndex)
} else {
onRemoveImage(item.originalIndex)
}
}

return (
<div className="flex items-center flex-wrap gap-1 mb-2">
{contextItems.map((item, index) => {
// Use nodeKey for mentions, fallback to type-index for images
const uniqueKey =
item.type === "mention" && item.nodeKey
? `mention-${item.nodeKey}`
: `${item.type}-${item.originalIndex}`

return (
<div
key={uniqueKey}
className={cn(
"relative flex items-center gap-1 px-2 py-1 border",
"bg-vscode-input-background text-vscode-input-foreground",
"rounded text-xs whitespace-nowrap flex-shrink-0 cursor-pointer",
"hover:bg-vscode-list-hoverBackground",
)}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}>
{hoveredIndex === index ? (
<button
onClick={(e) => {
e.stopPropagation()
handleRemove(item)
setHoveredIndex(null)
}}
className="flex shrink-0 items-center justify-center cursor-pointer">
<X className="size-3 text-vscode-input-foreground" />
</button>
) : (
<img
src={item.icon}
alt={item.iconAlt}
className="size-3 shrink-0"
style={{ ...item.iconStyle }}
/>
)}
<span>{item.displayName}</span>
</div>
)
})}
</div>
)
}
23 changes: 13 additions & 10 deletions webview-ui/src/components/chat/ChatRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,16 +176,19 @@ export const ChatRowContent = ({
}, [message.text, message.images, mode])

// Handle save edit
const handleSaveEdit = useCallback(() => {
setIsEditing(false)
// Send edited message to backend
vscode.postMessage({
type: "submitEditedMessage",
value: message.ts,
editedMessageContent: editedContent,
images: editImages,
})
}, [message.ts, editedContent, editImages])
const handleSaveEdit = useCallback(
(serializedContent: string) => {
setIsEditing(false)
// Send edited message to backend
vscode.postMessage({
type: "submitEditedMessage",
value: message.ts,
editedMessageContent: serializedContent,
images: editImages,
})
},
[message.ts, editImages],
)

// Handle image selection for editing
const handleSelectImages = useCallback(() => {
Expand Down
Loading
Loading