feat: add 5 lines context around search matches in accessibilitySnapshot, getCleanHTML, getLatestLogs

This commit is contained in:
Tommy D. Rossi
2026-01-12 15:36:51 +01:00
parent 2498b523a3
commit 7e7edac856
3 changed files with 94 additions and 18 deletions
+29 -6
View File
@@ -104,9 +104,10 @@ export async function getCleanHTML(options: GetCleanHTMLOptions): Promise<string
// Handle search // Handle search
if (search) { if (search) {
const lines = htmlStr.split('\n') const lines = htmlStr.split('\n')
const matches: string[] = [] const matchIndices: number[] = []
for (const line of lines) { for (let i = 0; i < lines.length; i++) {
const line = lines[i]
let isMatch = false let isMatch = false
if (isRegExp(search)) { if (isRegExp(search)) {
isMatch = search.test(line) isMatch = search.test(line)
@@ -115,16 +116,38 @@ export async function getCleanHTML(options: GetCleanHTMLOptions): Promise<string
} }
if (isMatch) { if (isMatch) {
matches.push(line) matchIndices.push(i)
if (matches.length >= 10) break if (matchIndices.length >= 10) break
} }
} }
if (matches.length === 0) { if (matchIndices.length === 0) {
return 'No matches found' return 'No matches found'
} }
return matches.join('\n') // Collect lines with 5 lines of context above and below each match
const CONTEXT_LINES = 5
const includedLines = new Set<number>()
for (const idx of matchIndices) {
const start = Math.max(0, idx - CONTEXT_LINES)
const end = Math.min(lines.length - 1, idx + CONTEXT_LINES)
for (let i = start; i <= end; i++) {
includedLines.add(i)
}
}
// Build result with separators between non-contiguous sections
const sortedIndices = [...includedLines].sort((a, b) => a - b)
const result: string[] = []
for (let i = 0; i < sortedIndices.length; i++) {
const lineIdx = sortedIndices[i]
if (i > 0 && sortedIndices[i - 1] !== lineIdx - 1) {
result.push('---')
}
result.push(lines[lineIdx])
}
return result.join('\n')
} }
return htmlStr return htmlStr
+2 -1
View File
@@ -979,7 +979,8 @@ describe('MCP Server Tests', () => {
const errorOutput = (errorLogsResult as any).content[0].text const errorOutput = (errorLogsResult as any).content[0].text
expect(errorOutput).toContain('[error] Test error 67890') expect(errorOutput).toContain('[error] Test error 67890')
expect(errorOutput).not.toContain('[log] Test log 12345') // With context lines (5 above/below), nearby logs are also included
expect(errorOutput).toContain('[log] Test log 12345')
// Test that logs are cleared on page reload // Test that logs are cleared on page reload
await client.callTool({ await client.callTool({
+63 -11
View File
@@ -747,9 +747,10 @@ server.tool(
} }
const lines = snapshotStr.split('\n') const lines = snapshotStr.split('\n')
const matches: string[] = [] const matchIndices: number[] = []
for (const line of lines) { for (let i = 0; i < lines.length; i++) {
const line = lines[i]
let isMatch = false let isMatch = false
if (isRegExp(search)) { if (isRegExp(search)) {
isMatch = search.test(line) isMatch = search.test(line)
@@ -758,16 +759,38 @@ server.tool(
} }
if (isMatch) { if (isMatch) {
matches.push(line) matchIndices.push(i)
if (matches.length >= 10) break if (matchIndices.length >= 10) break
} }
} }
if (matches.length === 0) { if (matchIndices.length === 0) {
return 'No matches found' return 'No matches found'
} }
return matches.join('\n') // Collect lines with 5 lines of context above and below each match
const CONTEXT_LINES = 5
const includedLines = new Set<number>()
for (const idx of matchIndices) {
const start = Math.max(0, idx - CONTEXT_LINES)
const end = Math.min(lines.length - 1, idx + CONTEXT_LINES)
for (let i = start; i <= end; i++) {
includedLines.add(i)
}
}
// Build result with separators between non-contiguous sections
const sortedIndices = [...includedLines].sort((a, b) => a - b)
const result: string[] = []
for (let i = 0; i < sortedIndices.length; i++) {
const lineIdx = sortedIndices[i]
if (i > 0 && sortedIndices[i - 1] !== lineIdx - 1) {
result.push('---')
}
result.push(lines[lineIdx])
}
return result.join('\n')
} }
throw new Error('accessibilitySnapshot is not available on this page') throw new Error('accessibilitySnapshot is not available on this page')
} }
@@ -812,14 +835,43 @@ server.tool(
} }
if (search) { if (search) {
allLogs = allLogs.filter((log) => { const matchIndices: number[] = []
for (let i = 0; i < allLogs.length; i++) {
const log = allLogs[i]
let isMatch = false
if (typeof search === 'string') { if (typeof search === 'string') {
return log.includes(search) isMatch = log.includes(search)
} else if (isRegExp(search)) { } else if (isRegExp(search)) {
return search.test(log) isMatch = search.test(log)
} }
return false if (isMatch) {
}) matchIndices.push(i)
}
}
// Collect logs with 5 lines of context above and below each match
const CONTEXT_LINES = 5
const includedIndices = new Set<number>()
for (const idx of matchIndices) {
const start = Math.max(0, idx - CONTEXT_LINES)
const end = Math.min(allLogs.length - 1, idx + CONTEXT_LINES)
for (let i = start; i <= end; i++) {
includedIndices.add(i)
}
}
// Build result with separators between non-contiguous sections
const sortedIndices = [...includedIndices].sort((a, b) => a - b)
const result: string[] = []
for (let i = 0; i < sortedIndices.length; i++) {
const logIdx = sortedIndices[i]
if (i > 0 && sortedIndices[i - 1] !== logIdx - 1) {
result.push('---')
}
result.push(allLogs[logIdx])
}
allLogs = result
} }
return count !== undefined ? allLogs.slice(-count) : allLogs return count !== undefined ? allLogs.slice(-count) : allLogs