feat(search): 效果五快捷栏加入本地搜索

- 引擎选项新增「本地」并作为默认;本地模式回车打开第一个匹配条目/分组
- 快捷栏改为贴在搜索框右侧内部展开,贴合效果五
- 外部引擎仍在新标签页搜索,输入时本地过滤逻辑保持
This commit is contained in:
2026-11-04 12:39:31 +08:00
parent 038b7ecb42
commit 8c1c658b15
2 changed files with 64 additions and 6 deletions
+15 -2
View File
@@ -1478,8 +1478,8 @@ html.theme-transition .site-background {
.engine-menu {
position: absolute;
top: calc(100% + 12px);
right: 0;
top: 50%;
right: calc(100% + 6px);
z-index: var(--su-z-float);
display: flex;
flex-direction: row;
@@ -1489,6 +1489,19 @@ html.theme-transition .site-background {
border-radius: var(--su-radius-sm);
background-color: var(--su-bg-card);
box-shadow: var(--su-shadow-pop);
transform: translateY(-50%);
animation: engine-bar-in 140ms var(--su-ease-out);
}
@keyframes engine-bar-in {
from {
opacity: 0;
transform: translate(6px, -50%);
}
to {
opacity: 1;
transform: translate(0, -50%);
}
}
.engine-option {
+49 -4
View File
@@ -63,6 +63,7 @@ type Route =
| { kind: 'api' }
const SEARCH_ENGINES = [
{ key: 'local', name: '本地', search: '' },
{ key: 'google', name: 'Google', search: 'https://www.google.com/search?q=' },
{ key: 'bing', name: 'Bing', search: 'https://www.bing.com/search?q=' },
{ key: 'baidu', name: '百度', search: 'https://www.baidu.com/s?wd=' },
@@ -202,6 +203,24 @@ function matches(entry: Record<string, unknown>, keyword: string): boolean {
return text.includes(keyword)
}
/** 本地搜索回车时打开第一个匹配项;没有条目命中但分组名命中时打开分组 */
function findLocalTarget(
groups: GroupNode[],
keyword: string,
): { group: GroupNode; entry?: Record<string, unknown> } | undefined {
for (const group of groups) {
if (group.enabled === false) continue
if (group.title.toLowerCase().includes(keyword)) return { group }
const entry = (group.entries as unknown as Record<string, unknown>[]).find((item) =>
matches(item, keyword),
)
if (entry !== undefined) return { group, entry }
const child = findLocalTarget(group.children, keyword)
if (child !== undefined) return child
}
return undefined
}
export default function AppNext() {
const { status, projects, error, live, refreshing, refresh } = useProjects()
const { route, navigate } = useRoute()
@@ -388,6 +407,20 @@ export default function AppNext() {
}
if (event.key !== 'Enter' || keyword.trim() === '') return
event.preventDefault()
setEngineOpen(false)
if (searchEngine.key === 'local') {
const target = findLocalTarget(manage.groups, keywordLower)
if (target !== undefined) {
navigate(
target.entry === undefined
? `/g/${encodeURIComponent(target.group.key)}`
: `/g/${encodeURIComponent(target.group.key)}/i/${encodeURIComponent(String(target.entry.id))}`,
)
}
return
}
window.open(
`${searchEngine.search}${encodeURIComponent(keyword.trim())}`,
'_blank',
@@ -396,7 +429,7 @@ export default function AppNext() {
}
input.addEventListener('keydown', handleKeyDown)
return () => input.removeEventListener('keydown', handleKeyDown)
}, [keyword, searchEngine])
}, [keyword, keywordLower, manage.groups, navigate, searchEngine])
const chooseEngine = (next: SearchEngine) => {
setSearchEngine(next)
@@ -797,8 +830,16 @@ export default function AppNext() {
value={keyword}
onChange={setKeyword}
inputRef={searchRef}
placeholder="搜索本地项目…"
label="搜索本地项目,回车使用当前搜索引擎"
placeholder={
searchEngine.key === 'local'
? '搜索本地项目…'
: `用 ${searchEngine.name} 搜索,回车打开…`
}
label={
searchEngine.key === 'local'
? '搜索本地项目,回车打开第一个匹配项'
: `用 ${searchEngine.name} 搜索,回车打开新标签页`
}
variant="card"
hotkey="k"
suffix={
@@ -809,7 +850,11 @@ export default function AppNext() {
aria-haspopup="menu"
aria-expanded={engineOpen}
aria-label={`搜索引擎:${searchEngine.name},点击切换`}
title={`当前:${searchEngine.name};回车用该引擎搜索`}
title={
searchEngine.key === 'local'
? '当前:本地搜索;回车打开第一个匹配项'
: `当前:${searchEngine.name};回车用该引擎搜索`
}
onClick={() => setEngineOpen((open) => !open)}
>
{searchEngine.name}