diff --git a/webview-ui/src/components/history/HistoryView.tsx b/webview-ui/src/components/history/HistoryView.tsx
index 13150f9..82b6d2f 100644
--- a/webview-ui/src/components/history/HistoryView.tsx
+++ b/webview-ui/src/components/history/HistoryView.tsx
@@ -5,6 +5,7 @@ import { Virtuoso } from "react-virtuoso"
import React, { memo, useMemo, useState, useEffect } from "react"
import { Fzf } from "fzf"
import { formatLargeNumber } from "../../utils/format"
+import { highlightFzfMatch } from "../../utils/highlight"
type HistoryViewProps = {
onDone: () => void
@@ -464,49 +465,4 @@ const ExportButton = ({ itemId }: { itemId: string }) => (
)
-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
- ? `${part.text}`
- : part.text
- )
- .join('')
-}
-
export default memo(HistoryView)
diff --git a/webview-ui/src/components/settings/GlamaModelPicker.tsx b/webview-ui/src/components/settings/GlamaModelPicker.tsx
index 5164ba5..de93b9c 100644
--- a/webview-ui/src/components/settings/GlamaModelPicker.tsx
+++ b/webview-ui/src/components/settings/GlamaModelPicker.tsx
@@ -7,6 +7,7 @@ import styled from "styled-components"
import { glamaDefaultModelId } from "../../../../src/shared/api"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { vscode } from "../../utils/vscode"
+import { highlightFzfMatch } from "../../utils/highlight"
import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions"
const GlamaModelPicker: React.FC = () => {
@@ -71,51 +72,6 @@ const GlamaModelPicker: React.FC = () => {
}))
}, [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
- ? `${part.text}`
- : part.text
- )
- .join('')
- }
-
const fzf = useMemo(() => {
return new Fzf(searchableItems, {
selector: item => item.html
@@ -128,7 +84,7 @@ const GlamaModelPicker: React.FC = () => {
const searchResults = fzf.find(searchTerm)
return searchResults.map(result => ({
...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])
diff --git a/webview-ui/src/components/settings/OpenAiModelPicker.tsx b/webview-ui/src/components/settings/OpenAiModelPicker.tsx
index 2e674dd..7e8a81f 100644
--- a/webview-ui/src/components/settings/OpenAiModelPicker.tsx
+++ b/webview-ui/src/components/settings/OpenAiModelPicker.tsx
@@ -5,6 +5,7 @@ import { useRemark } from "react-remark"
import styled from "styled-components"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { vscode } from "../../utils/vscode"
+import { highlightFzfMatch } from "../../utils/highlight"
const OpenAiModelPicker: React.FC = () => {
const { apiConfiguration, setApiConfiguration, openAiModels, onUpdateApiConfig } = useExtensionState()
@@ -70,51 +71,6 @@ const OpenAiModelPicker: React.FC = () => {
}))
}, [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
- ? `${part.text}`
- : part.text
- )
- .join('')
- }
-
const fzf = useMemo(() => {
return new Fzf(searchableItems, {
selector: item => item.html
@@ -127,7 +83,7 @@ const OpenAiModelPicker: React.FC = () => {
const searchResults = fzf.find(searchTerm)
return searchResults.map(result => ({
...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])
diff --git a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx
index 13e308d..f164fb3 100644
--- a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx
+++ b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx
@@ -7,6 +7,7 @@ import styled from "styled-components"
import { openRouterDefaultModelId } from "../../../../src/shared/api"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { vscode } from "../../utils/vscode"
+import { highlightFzfMatch } from "../../utils/highlight"
import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions"
const OpenRouterModelPicker: React.FC = () => {
@@ -70,51 +71,6 @@ const OpenRouterModelPicker: React.FC = () => {
}))
}, [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
- ? `${part.text}`
- : part.text
- )
- .join('')
- }
-
const fzf = useMemo(() => {
return new Fzf(searchableItems, {
selector: item => item.html
@@ -127,7 +83,7 @@ const OpenRouterModelPicker: React.FC = () => {
const searchResults = fzf.find(searchTerm)
return searchResults.map(result => ({
...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])
diff --git a/webview-ui/src/utils/highlight.ts b/webview-ui/src/utils/highlight.ts
new file mode 100644
index 0000000..a6bbf76
--- /dev/null
+++ b/webview-ui/src/utils/highlight.ts
@@ -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
+ ? `${part.text}`
+ : part.text
+ )
+ .join('')
+}
\ No newline at end of file