mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
DRY up highlighting code
This commit is contained in:
@@ -5,6 +5,7 @@ import { Virtuoso } from "react-virtuoso"
|
|||||||
import React, { memo, useMemo, useState, useEffect } from "react"
|
import React, { memo, useMemo, useState, useEffect } from "react"
|
||||||
import { Fzf } from "fzf"
|
import { Fzf } from "fzf"
|
||||||
import { formatLargeNumber } from "../../utils/format"
|
import { formatLargeNumber } from "../../utils/format"
|
||||||
|
import { highlightFzfMatch } from "../../utils/highlight"
|
||||||
|
|
||||||
type HistoryViewProps = {
|
type HistoryViewProps = {
|
||||||
onDone: () => void
|
onDone: () => void
|
||||||
@@ -464,49 +465,4 @@ const ExportButton = ({ itemId }: { itemId: string }) => (
|
|||||||
</VSCodeButton>
|
</VSCodeButton>
|
||||||
)
|
)
|
||||||
|
|
||||||
const highlightFzfMatch = (text: string, positions: number[], highlightClassName: string = "history-item-highlight") => {
|
|
||||||
if (!positions.length) return text
|
|
||||||
|
|
||||||
const parts: { text: string; highlight: boolean }[] = []
|
|
||||||
let lastIndex = 0
|
|
||||||
|
|
||||||
// Sort positions to ensure we process them in order
|
|
||||||
positions.sort((a, b) => a - b)
|
|
||||||
|
|
||||||
positions.forEach((pos) => {
|
|
||||||
// Add non-highlighted text before this position
|
|
||||||
if (pos > lastIndex) {
|
|
||||||
parts.push({
|
|
||||||
text: text.substring(lastIndex, pos),
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add highlighted character
|
|
||||||
parts.push({
|
|
||||||
text: text[pos],
|
|
||||||
highlight: true
|
|
||||||
})
|
|
||||||
|
|
||||||
lastIndex = pos + 1
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add any remaining text
|
|
||||||
if (lastIndex < text.length) {
|
|
||||||
parts.push({
|
|
||||||
text: text.substring(lastIndex),
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build final string
|
|
||||||
return parts
|
|
||||||
.map(part =>
|
|
||||||
part.highlight
|
|
||||||
? `<span class="${highlightClassName}">${part.text}</span>`
|
|
||||||
: part.text
|
|
||||||
)
|
|
||||||
.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(HistoryView)
|
export default memo(HistoryView)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import styled from "styled-components"
|
|||||||
import { glamaDefaultModelId } from "../../../../src/shared/api"
|
import { glamaDefaultModelId } from "../../../../src/shared/api"
|
||||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||||
import { vscode } from "../../utils/vscode"
|
import { vscode } from "../../utils/vscode"
|
||||||
|
import { highlightFzfMatch } from "../../utils/highlight"
|
||||||
import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions"
|
import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions"
|
||||||
|
|
||||||
const GlamaModelPicker: React.FC = () => {
|
const GlamaModelPicker: React.FC = () => {
|
||||||
@@ -71,51 +72,6 @@ const GlamaModelPicker: React.FC = () => {
|
|||||||
}))
|
}))
|
||||||
}, [modelIds])
|
}, [modelIds])
|
||||||
|
|
||||||
const highlightFzfMatch = (text: string, positions: number[]) => {
|
|
||||||
if (!positions.length) return text
|
|
||||||
|
|
||||||
const parts: { text: string; highlight: boolean }[] = []
|
|
||||||
let lastIndex = 0
|
|
||||||
|
|
||||||
// Sort positions to ensure we process them in order
|
|
||||||
positions.sort((a, b) => a - b)
|
|
||||||
|
|
||||||
positions.forEach((pos) => {
|
|
||||||
// Add non-highlighted text before this position
|
|
||||||
if (pos > lastIndex) {
|
|
||||||
parts.push({
|
|
||||||
text: text.substring(lastIndex, pos),
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add highlighted character
|
|
||||||
parts.push({
|
|
||||||
text: text[pos],
|
|
||||||
highlight: true
|
|
||||||
})
|
|
||||||
|
|
||||||
lastIndex = pos + 1
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add any remaining text
|
|
||||||
if (lastIndex < text.length) {
|
|
||||||
parts.push({
|
|
||||||
text: text.substring(lastIndex),
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build final string
|
|
||||||
return parts
|
|
||||||
.map(part =>
|
|
||||||
part.highlight
|
|
||||||
? `<span class="model-item-highlight">${part.text}</span>`
|
|
||||||
: part.text
|
|
||||||
)
|
|
||||||
.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
const fzf = useMemo(() => {
|
const fzf = useMemo(() => {
|
||||||
return new Fzf(searchableItems, {
|
return new Fzf(searchableItems, {
|
||||||
selector: item => item.html
|
selector: item => item.html
|
||||||
@@ -128,7 +84,7 @@ const GlamaModelPicker: React.FC = () => {
|
|||||||
const searchResults = fzf.find(searchTerm)
|
const searchResults = fzf.find(searchTerm)
|
||||||
return searchResults.map(result => ({
|
return searchResults.map(result => ({
|
||||||
...result.item,
|
...result.item,
|
||||||
html: highlightFzfMatch(result.item.html, Array.from(result.positions))
|
html: highlightFzfMatch(result.item.html, Array.from(result.positions), "model-item-highlight")
|
||||||
}))
|
}))
|
||||||
}, [searchableItems, searchTerm, fzf])
|
}, [searchableItems, searchTerm, fzf])
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useRemark } from "react-remark"
|
|||||||
import styled from "styled-components"
|
import styled from "styled-components"
|
||||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||||
import { vscode } from "../../utils/vscode"
|
import { vscode } from "../../utils/vscode"
|
||||||
|
import { highlightFzfMatch } from "../../utils/highlight"
|
||||||
|
|
||||||
const OpenAiModelPicker: React.FC = () => {
|
const OpenAiModelPicker: React.FC = () => {
|
||||||
const { apiConfiguration, setApiConfiguration, openAiModels, onUpdateApiConfig } = useExtensionState()
|
const { apiConfiguration, setApiConfiguration, openAiModels, onUpdateApiConfig } = useExtensionState()
|
||||||
@@ -70,51 +71,6 @@ const OpenAiModelPicker: React.FC = () => {
|
|||||||
}))
|
}))
|
||||||
}, [modelIds])
|
}, [modelIds])
|
||||||
|
|
||||||
const highlightFzfMatch = (text: string, positions: number[]) => {
|
|
||||||
if (!positions.length) return text
|
|
||||||
|
|
||||||
const parts: { text: string; highlight: boolean }[] = []
|
|
||||||
let lastIndex = 0
|
|
||||||
|
|
||||||
// Sort positions to ensure we process them in order
|
|
||||||
positions.sort((a, b) => a - b)
|
|
||||||
|
|
||||||
positions.forEach((pos) => {
|
|
||||||
// Add non-highlighted text before this position
|
|
||||||
if (pos > lastIndex) {
|
|
||||||
parts.push({
|
|
||||||
text: text.substring(lastIndex, pos),
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add highlighted character
|
|
||||||
parts.push({
|
|
||||||
text: text[pos],
|
|
||||||
highlight: true
|
|
||||||
})
|
|
||||||
|
|
||||||
lastIndex = pos + 1
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add any remaining text
|
|
||||||
if (lastIndex < text.length) {
|
|
||||||
parts.push({
|
|
||||||
text: text.substring(lastIndex),
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build final string
|
|
||||||
return parts
|
|
||||||
.map(part =>
|
|
||||||
part.highlight
|
|
||||||
? `<span class="model-item-highlight">${part.text}</span>`
|
|
||||||
: part.text
|
|
||||||
)
|
|
||||||
.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
const fzf = useMemo(() => {
|
const fzf = useMemo(() => {
|
||||||
return new Fzf(searchableItems, {
|
return new Fzf(searchableItems, {
|
||||||
selector: item => item.html
|
selector: item => item.html
|
||||||
@@ -127,7 +83,7 @@ const OpenAiModelPicker: React.FC = () => {
|
|||||||
const searchResults = fzf.find(searchTerm)
|
const searchResults = fzf.find(searchTerm)
|
||||||
return searchResults.map(result => ({
|
return searchResults.map(result => ({
|
||||||
...result.item,
|
...result.item,
|
||||||
html: highlightFzfMatch(result.item.html, Array.from(result.positions))
|
html: highlightFzfMatch(result.item.html, Array.from(result.positions), "model-item-highlight")
|
||||||
}))
|
}))
|
||||||
}, [searchableItems, searchTerm, fzf])
|
}, [searchableItems, searchTerm, fzf])
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import styled from "styled-components"
|
|||||||
import { openRouterDefaultModelId } from "../../../../src/shared/api"
|
import { openRouterDefaultModelId } from "../../../../src/shared/api"
|
||||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||||
import { vscode } from "../../utils/vscode"
|
import { vscode } from "../../utils/vscode"
|
||||||
|
import { highlightFzfMatch } from "../../utils/highlight"
|
||||||
import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions"
|
import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions"
|
||||||
|
|
||||||
const OpenRouterModelPicker: React.FC = () => {
|
const OpenRouterModelPicker: React.FC = () => {
|
||||||
@@ -70,51 +71,6 @@ const OpenRouterModelPicker: React.FC = () => {
|
|||||||
}))
|
}))
|
||||||
}, [modelIds])
|
}, [modelIds])
|
||||||
|
|
||||||
const highlightFzfMatch = (text: string, positions: number[]) => {
|
|
||||||
if (!positions.length) return text
|
|
||||||
|
|
||||||
const parts: { text: string; highlight: boolean }[] = []
|
|
||||||
let lastIndex = 0
|
|
||||||
|
|
||||||
// Sort positions to ensure we process them in order
|
|
||||||
positions.sort((a, b) => a - b)
|
|
||||||
|
|
||||||
positions.forEach((pos) => {
|
|
||||||
// Add non-highlighted text before this position
|
|
||||||
if (pos > lastIndex) {
|
|
||||||
parts.push({
|
|
||||||
text: text.substring(lastIndex, pos),
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add highlighted character
|
|
||||||
parts.push({
|
|
||||||
text: text[pos],
|
|
||||||
highlight: true
|
|
||||||
})
|
|
||||||
|
|
||||||
lastIndex = pos + 1
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add any remaining text
|
|
||||||
if (lastIndex < text.length) {
|
|
||||||
parts.push({
|
|
||||||
text: text.substring(lastIndex),
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build final string
|
|
||||||
return parts
|
|
||||||
.map(part =>
|
|
||||||
part.highlight
|
|
||||||
? `<span class="model-item-highlight">${part.text}</span>`
|
|
||||||
: part.text
|
|
||||||
)
|
|
||||||
.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
const fzf = useMemo(() => {
|
const fzf = useMemo(() => {
|
||||||
return new Fzf(searchableItems, {
|
return new Fzf(searchableItems, {
|
||||||
selector: item => item.html
|
selector: item => item.html
|
||||||
@@ -127,7 +83,7 @@ const OpenRouterModelPicker: React.FC = () => {
|
|||||||
const searchResults = fzf.find(searchTerm)
|
const searchResults = fzf.find(searchTerm)
|
||||||
return searchResults.map(result => ({
|
return searchResults.map(result => ({
|
||||||
...result.item,
|
...result.item,
|
||||||
html: highlightFzfMatch(result.item.html, Array.from(result.positions))
|
html: highlightFzfMatch(result.item.html, Array.from(result.positions), "model-item-highlight")
|
||||||
}))
|
}))
|
||||||
}, [searchableItems, searchTerm, fzf])
|
}, [searchableItems, searchTerm, fzf])
|
||||||
|
|
||||||
|
|||||||
44
webview-ui/src/utils/highlight.ts
Normal file
44
webview-ui/src/utils/highlight.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
export function highlightFzfMatch(text: string, positions: number[], highlightClassName: string = "history-item-highlight") {
|
||||||
|
if (!positions.length) return text
|
||||||
|
|
||||||
|
const parts: { text: string; highlight: boolean }[] = []
|
||||||
|
let lastIndex = 0
|
||||||
|
|
||||||
|
// Sort positions to ensure we process them in order
|
||||||
|
positions.sort((a, b) => a - b)
|
||||||
|
|
||||||
|
positions.forEach((pos) => {
|
||||||
|
// Add non-highlighted text before this position
|
||||||
|
if (pos > lastIndex) {
|
||||||
|
parts.push({
|
||||||
|
text: text.substring(lastIndex, pos),
|
||||||
|
highlight: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add highlighted character
|
||||||
|
parts.push({
|
||||||
|
text: text[pos],
|
||||||
|
highlight: true
|
||||||
|
})
|
||||||
|
|
||||||
|
lastIndex = pos + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add any remaining text
|
||||||
|
if (lastIndex < text.length) {
|
||||||
|
parts.push({
|
||||||
|
text: text.substring(lastIndex),
|
||||||
|
highlight: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build final string
|
||||||
|
return parts
|
||||||
|
.map(part =>
|
||||||
|
part.highlight
|
||||||
|
? `<span class="${highlightClassName}">${part.text}</span>`
|
||||||
|
: part.text
|
||||||
|
)
|
||||||
|
.join('')
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user