Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,54 @@
# Changelog

## [v5.2.5] - 2026-01-21

### Added

- Multi-line chip rendering for improved mention display

### Changed

- Clean up profile page
- Enhanced chat rendering utilities

### Fixed

- Fixed plan hover popup display

---

## [v5.2.4] - 2026-01-19

### Fixed

- Fixed SourceControlPanel.tsx - addressed review comments
- Fixed chat text UI issues

---

## [v5.2.3] - 2026-01-16

### Fixed

- Fixed chat text UI with improved component structure

---

## [v5.2.2] - 2026-01-16

### Added

- Model selector moved to improved location

### Changed

- UI improvements and IDE theme fixes
- Enhanced ChatTextArea component
- Updated translations for all supported languages
- Improved select-dropdown and popover components

---

## [v5.2.1] - 2026-01-15

### Added
Expand Down
33 changes: 29 additions & 4 deletions src/core/mentions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,21 @@ async function getFileOrFolderContent(
showRooIgnoredFiles: boolean = false,
maxReadFileLine?: number,
): Promise<string> {
const unescapedPath = unescapeSpaces(mentionPath)
// Parse line numbers from the mention path (e.g., "file.ts#L20-80")
const lineMatch = mentionPath.match(/^(.*?)(?:#L(\d+)(?:-(\d+))?)?$/)
let filePath = mentionPath
let startLine: number | undefined
let endLine: number | undefined

if (lineMatch) {
filePath = lineMatch[1]
if (lineMatch[2]) {
startLine = parseInt(lineMatch[2], 10)
endLine = lineMatch[3] ? parseInt(lineMatch[3], 10) : startLine
}
}

const unescapedPath = unescapeSpaces(filePath)
const absPath = path.resolve(cwd, unescapedPath)

try {
Expand All @@ -285,9 +299,20 @@ async function getFileOrFolderContent(
// kilocode_change end
try {
const content = await extractTextFromFile(absPath, maxReadFileLine)

// Extract specific lines if line numbers are specified
if (startLine !== undefined && endLine !== undefined) {
const lines = content.split("\n")
// Convert to 0-based index
const startIndex = Math.max(0, startLine - 1)
const endIndex = Math.min(lines.length, endLine)
const extractedLines = lines.slice(startIndex, endIndex)
return extractedLines.join("\n")
}

return content
} catch (error) {
return `(Failed to read contents of ${mentionPath}): ${error.message}`
return `(Failed to read contents of ${filePath}): ${error.message}`
}
} else if (stats.isDirectory()) {
const entries = await fs.readdir(absPath, { withFileTypes: true })
Expand Down Expand Up @@ -341,10 +366,10 @@ async function getFileOrFolderContent(
const fileContents = (await Promise.all(fileContentPromises)).filter((content) => content)
return `${folderContent}\n${fileContents.join("\n\n")}`.trim()
} else {
return `(Failed to read contents of ${mentionPath})`
return `(Failed to read contents of ${filePath})`
}
} catch (error) {
throw new Error(`Failed to access path "${mentionPath}": ${error.message}`)
throw new Error(`Failed to access path "${filePath}": ${error.message}`)
}
}

Expand Down
18 changes: 9 additions & 9 deletions src/shared/__tests__/support-prompts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ describe("Code Action Prompts", () => {
endLine: "1",
diagnostics: [],
})
const expected = `${testFilePath}:1-1\n\`\`\`\n${testCode}\n\`\`\``
const expected = `@/${testFilePath}#L1-1`
expect(prompt).toBe(expected)
})

Expand All @@ -110,7 +110,7 @@ describe("Code Action Prompts", () => {
endLine: "20",
diagnostics,
})
const expected = `${testFilePath}:10-20\n\`\`\`\n${testCode}\n\`\`\``
const expected = `@/${testFilePath}#L10-20`
expect(prompt).toBe(expected)
})

Expand All @@ -123,7 +123,7 @@ describe("Code Action Prompts", () => {
startLine: "5",
endLine: "15",
})
const expected = `${testFilePath}:5-15\n\`\`\`\n${testCode}\n\`\`\``
const expected = `@/${testFilePath}#L5-15`
expect(prompt).toBe(expected)
})

Expand All @@ -135,7 +135,7 @@ describe("Code Action Prompts", () => {
startLine: "1",
endLine: "1",
}) // 'status' is missing
const expected = `${testFilePath}:1-1\n\`\`\`\n${testCode}\n\`\`\``
const expected = `@/${testFilePath}#L1-1`
expect(prompt).toBe(expected)
})

Expand All @@ -147,7 +147,7 @@ describe("Code Action Prompts", () => {
startLine: "1",
endLine: "1",
})
const expected = `${testFilePath}:1-1\n\`\`\`\n${testCode}\n\`\`\``
const expected = `@/${testFilePath}#L1-1`
expect(prompt).toBe(expected)
})

Expand All @@ -158,7 +158,7 @@ describe("Code Action Prompts", () => {
startLine: "1",
endLine: "1",
})
const expected = `${testFilePath}:1-1\n\`\`\`\n${testCode}\n\`\`\``
const expected = `@/${testFilePath}#L1-1`
expect(prompt).toBe(expected)
})

Expand All @@ -171,7 +171,7 @@ describe("Code Action Prompts", () => {
startLine: "1",
endLine: "1",
}) // Convert to strings
const expected = `${testFilePath}:1-1\n\`\`\`\n${testCode}\n\`\`\``
const expected = `@/${testFilePath}#L1-1`
expect(prompt).toBe(expected)
})

Expand All @@ -184,7 +184,7 @@ describe("Code Action Prompts", () => {
startLine: "1",
endLine: "1",
})
const expected = `${testFilePath}:1-1\n\`\`\`\n${testCode}\n\`\`\``
const expected = `@/${testFilePath}#L1-1`
expect(prompt).toBe(expected)
})

Expand All @@ -198,7 +198,7 @@ describe("Code Action Prompts", () => {
endLine: "1",
diagnostics: [],
})
const expected = `${testFilePath}:1-1\n\`\`\`\n${bashText}\n\`\`\``
const expected = `@/${testFilePath}#L1-1`
expect(prompt).toBe(expected)
})
})
Expand Down
16 changes: 11 additions & 5 deletions src/shared/context-mentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ Mention regex:
- `(?:^|\s)`:
- **Non-Capturing Group (`(?:...)`)**: Groups the alternatives without capturing them.
- **Line Start or Whitespace (`^|\s`)**: The @ must be at the start of a line or preceded by whitespace.

- `(?<!\\)@`:
- **Negative Lookbehind (`(?<!\\)`)**: Ensures the @ is not escaped with a backslash.
- **@**: The mention must start with the '@' symbol.
- `((?:\/|\w+:\/\/)[^\s]+?|problems\b|git-changes\b)`:

- `((?:\/|\w+:\/\/)(?:[^\s\\]|\\ )+?(?:#L\d+(?:-\d+)?)?|[a-f0-9]{7,40}\b|problems\b|git-changes\b|terminal\b)`:
- **Capturing Group (`(...)`)**: Captures the part of the string that matches one of the specified patterns.
- `(?:\/|\w+:\/\/)`:
- **Non-Capturing Group (`(?:...)`)**: Groups the alternatives without capturing them for back-referencing.
Expand All @@ -30,6 +30,11 @@ Mention regex:
- **OR (`|`)**: Logical OR.
- **Escaped Space (`\\ `)**: Matches a backslash followed by a space (an escaped space).
- **Non-Greedy (`+?`)**: Ensures the smallest possible match, preventing the inclusion of trailing punctuation.
- `(?:#L\d+(?:-\d+)?)?`:
- **Optional Non-Capturing Group (`(?:...)?`)**: Optionally matches line number syntax.
- `#L`: Matches the literal string '#L'.
- `\d+`: Matches one or more digits (the start line number).
- `(?:-\d+)?`: Optionally matches a dash followed by one or more digits (the end line number).
- `|`: Logical OR.
- `problems\b`:
- **Exact Word ('problems')**: Matches the exact word 'problems'.
Expand All @@ -44,11 +49,12 @@ Mention regex:
- **Optional Punctuation (`[.,;:!?]?`)**: Matches zero or one of the specified punctuation marks.
- `(?=[\s\r\n]|$)`:
- **Nested Positive Lookahead (`(?=[\s\r\n]|$)`)**: Ensures that the punctuation (if present) is followed by a whitespace character, a line break, or the end of the string.

- **Summary**:
- The regex effectively matches:
- Mentions that are file or folder paths starting with '/' and containing any non-whitespace characters (including periods within the path).
- File paths can include spaces if they are escaped with a backslash (e.g., `@/path/to/file\ with\ spaces.txt`).
- File paths can optionally include line numbers in the format `#L20` or `#L20-80`.
- URLs that start with a protocol (like 'http://') followed by any non-whitespace characters (including query parameters).
- The exact word 'problems'.
- The exact word 'git-changes'.
Expand All @@ -61,7 +67,7 @@ Mention regex:

*/
export const mentionRegex =
/(?:^|(?<=\s))(?<!\\)@((?:\/|\w+:\/\/)(?:[^\s\\]|\\ )+?|[a-f0-9]{7,40}\b|problems\b|git-changes\b|terminal\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/
/(?:^|(?<=\s))(?<!\\)@((?:\/|\w+:\/\/)(?:[^\s\\]|\\ )+?(?:#L\d+(?:-\d+)?)?|[a-f0-9]{7,40}\b|problems\b|git-changes\b|terminal\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/
export const mentionRegexGlobal = new RegExp(mentionRegex.source, "g")

// Regex to match command mentions like /command-name anywhere in text
Expand Down
5 changes: 1 addition & 4 deletions src/shared/support-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,7 @@ Please suggest improvements for:
Provide the improved code along with explanations for each enhancement.`,
},
ADD_TO_CONTEXT: {
template: `\${filePath}:\${startLine}-\${endLine}
\`\`\`
\${selectedText}
\`\`\``,
template: `@/\${filePath}#L\${startLine}-\${endLine}`,
},
TERMINAL_ADD_TO_CONTEXT: {
template: `\${userInput}
Expand Down
5 changes: 5 additions & 0 deletions webview-ui/src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,11 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
newValue = inputValue + " " + text
}

// Add a space after the chip so cursor can be positioned after it
if (!newValue.endsWith(" ")) {
newValue += " "
}

setInputValue(newValue)
setSelectedImages([...selectedImages, ...images])
},
Expand Down
16 changes: 9 additions & 7 deletions webview-ui/src/components/chat/CodeIndexPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -706,13 +706,15 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
{t("settings:unsavedChangesDialog.description")}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => onConfirmDialogResult(false)}>
{t("settings:unsavedChangesDialog.cancelButton")}
</AlertDialogCancel>
<AlertDialogAction onClick={() => onConfirmDialogResult(true)}>
{t("settings:unsavedChangesDialog.discardButton")}
</AlertDialogAction>
<AlertDialogFooter className="flex-col gap-2 items-center justify-center">
<div className="flex gap-1 flex-col">
<AlertDialogCancel onClick={() => onConfirmDialogResult(false)}>
{t("settings:unsavedChangesDialog.cancelButton")}
</AlertDialogCancel>
<AlertDialogAction onClick={() => onConfirmDialogResult(true)}>
{t("settings:unsavedChangesDialog.discardButton")}
</AlertDialogAction>
</div>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/components/kilocode/BottomApiConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const BottomApiConfig = () => {
const rect = triggerRef.current.getBoundingClientRect()
setCardPosition({
top: rect.top - 10,
left: rect.left + rect.width / 2,
left: rect.left,
})
}
setShowHoverCard(true)
Expand Down Expand Up @@ -121,7 +121,7 @@ export const BottomApiConfig = () => {
style={{
top: `${cardPosition.top}px`,
left: `${cardPosition.left}px`,
transform: "translate(-50%, -100%)",
transform: "translate(0, -100%)",
}}>
<div className="space-y-3">
<div className="space-y-1">
Expand Down
Loading
Loading