chore: 初始化项目仓库
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { extractTextFromAst } from '~/utils/content-ast'
|
||||
|
||||
export interface PostSearchFilters {
|
||||
title: boolean
|
||||
description: boolean
|
||||
content: boolean
|
||||
tags: boolean
|
||||
}
|
||||
|
||||
export interface PostSearchResult {
|
||||
post: any
|
||||
matchedLines: string[]
|
||||
}
|
||||
|
||||
function parseQueryTerms(query: string): string[] {
|
||||
const terms: string[] = []
|
||||
const re = /"([^"]+)"|(\S+)/g
|
||||
let m: RegExpExecArray | null
|
||||
while ((m = re.exec(query)) !== null) {
|
||||
const t = (m[1] ?? m[2] ?? '').trim().toLowerCase()
|
||||
if (t) terms.push(t)
|
||||
}
|
||||
return terms
|
||||
}
|
||||
|
||||
function highlightText(text: string, terms: string[]): string {
|
||||
if (!terms.length) return text
|
||||
const escaped = terms
|
||||
.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
||||
.sort((a, b) => b.length - a.length)
|
||||
const regex = new RegExp(`(${escaped.join('|')})`, 'gi')
|
||||
return text.replace(regex, '<mark class="search-highlight">$1</mark>')
|
||||
}
|
||||
|
||||
function getMatchedLines(plainText: string, terms: string[]): string[] {
|
||||
if (!terms.length || !plainText) return []
|
||||
const lines = plainText.split('\n')
|
||||
const matched: string[] = []
|
||||
for (const line of lines) {
|
||||
const lower = line.toLowerCase()
|
||||
if (terms.every((t) => lower.includes(t))) {
|
||||
const trimmed = line.trim()
|
||||
if (
|
||||
trimmed
|
||||
&& trimmed.length > 10
|
||||
&& !trimmed.startsWith('#')
|
||||
&& !trimmed.startsWith('.')
|
||||
&& !trimmed.includes('{')
|
||||
&& !trimmed.includes('}')
|
||||
&& !trimmed.includes(':')
|
||||
&& !trimmed.includes(';')
|
||||
&& !trimmed.match(/^[a-z][\w-]*[\s]*\{/)
|
||||
&& !trimmed.match(/^\.shiki/)
|
||||
&& !trimmed.match(/^var\(--/)
|
||||
&& !trimmed.match(/^html\b/)
|
||||
&& !trimmed.match(/^pre\b/)
|
||||
&& !trimmed.match(/^@/)
|
||||
) {
|
||||
matched.push(trimmed)
|
||||
}
|
||||
}
|
||||
}
|
||||
return matched.slice(0, 10)
|
||||
}
|
||||
|
||||
export function usePostSearch(
|
||||
allPosts: Ref<any[] | null>,
|
||||
searchQuery: Ref<string>,
|
||||
filters: Ref<PostSearchFilters>,
|
||||
) {
|
||||
const searchResults = computed<PostSearchResult[]>(() => {
|
||||
if (!allPosts.value || !searchQuery.value.trim()) {
|
||||
return allPosts.value?.map((p) => ({ post: p, matchedLines: [] })) || []
|
||||
}
|
||||
|
||||
const terms = parseQueryTerms(searchQuery.value)
|
||||
if (!terms.length) return allPosts.value.map((p) => ({ post: p, matchedLines: [] }))
|
||||
|
||||
const results: PostSearchResult[] = []
|
||||
|
||||
for (const post of allPosts.value) {
|
||||
const title = (post.title || '').toLowerCase()
|
||||
const desc = (post.description || '').toLowerCase()
|
||||
|
||||
const matchTitle = filters.value.title && terms.some((t) => title.includes(t))
|
||||
const matchDesc = filters.value.description && terms.some((t) => desc.includes(t))
|
||||
const matchTags = filters.value.tags && (post.tags || []).some((tag: string) => terms.some((t) => tag.toLowerCase().includes(t)))
|
||||
|
||||
let matchContent = false
|
||||
let matchedLines: string[] = []
|
||||
if (filters.value.content) {
|
||||
const plainText = extractTextFromAst(post.body?.value || post.body || null).toLowerCase()
|
||||
matchContent = terms.some((t) => plainText.includes(t))
|
||||
if (matchContent) {
|
||||
matchedLines = getMatchedLines(
|
||||
extractTextFromAst(post.body?.value || post.body || null),
|
||||
terms,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (matchTitle || matchDesc || matchContent || matchTags) {
|
||||
results.push({ post, matchedLines })
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
})
|
||||
|
||||
const filteredPosts = computed(() => searchResults.value.map((r) => r.post))
|
||||
|
||||
function highlight(text: string): string {
|
||||
if (!searchQuery.value.trim()) return text
|
||||
return highlightText(text, parseQueryTerms(searchQuery.value))
|
||||
}
|
||||
|
||||
function getTerms(): string[] {
|
||||
return parseQueryTerms(searchQuery.value)
|
||||
}
|
||||
|
||||
return {
|
||||
searchResults,
|
||||
filteredPosts,
|
||||
highlight,
|
||||
getTerms,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user