✨ feat(vscode): 摘修复可按作者筛选,高亮发布点后第一条

This commit is contained in:
cruldra committed 2026-09-01 19:15:32 +08:00
1 parent 54fc2ead4c
commit beb7750eac
9 files changed
+137 -33

No files matched your search

@@ -1,11 +1,13 @@
import type { ReleaseCommitGroup } from '../lib/messages'
import { ChevronRight, X } from 'lucide-react'
import { useEffect, useMemo, useState } from 'react'
import { filterCherryPickGroups, groupContainingSha, uniqueAuthors } from '../lib/cherryPickView'
interface Props {
open: boolean
branch: string
groups: ReleaseCommitGroup[]
firstAfterReleaseSha?: string
onCancel: () => void
onConfirm: (shas: string[]) => void
}
@@ -14,38 +16,25 @@ function groupKey(g: ReleaseCommitGroup): string {
return g.pr != null ? `pr-${g.pr}` : 'ungrouped'
}
function filterGroups(groups: ReleaseCommitGroup[], query: string): ReleaseCommitGroup[] {
const q = query.trim().toLowerCase()
if (!q)
return groups
const out: ReleaseCommitGroup[] = []
for (const g of groups) {
const head = `#${g.pr ?? ''} ${g.title} ${g.authors.join(' ')}`.toLowerCase()
if (head.includes(q)) {
out.push(g)
continue
}
const commits = g.commits.filter(c =>
`${c.subject} ${c.author} ${c.sha} ${c.date}`.toLowerCase().includes(q),
)
if (commits.length > 0)
out.push({ ...g, commits })
}
return out
}
export function CherryPickModal({ open, branch, groups, onCancel, onConfirm }: Props) {
export function CherryPickModal({ open, branch, groups, firstAfterReleaseSha, onCancel, onConfirm }: Props) {
const authors = useMemo(() => uniqueAuthors(groups), [groups])
const [query, setQuery] = useState('')
const [selected, setSelected] = useState<Set<string>>(() => new Set())
const [expanded, setExpanded] = useState<Set<string>>(() => new Set())
const [enabledAuthors, setEnabledAuthors] = useState<Set<string>>(() => new Set())
useEffect(() => {
if (!open)
return
setQuery('')
setSelected(new Set())
setExpanded(new Set(groups.filter(g => g.pr == null).map(groupKey)))
}, [open, groups])
setEnabledAuthors(new Set(uniqueAuthors(groups)))
const keys = new Set(groups.filter(g => g.pr == null).map(groupKey))
const marked = groupContainingSha(groups, firstAfterReleaseSha)
if (marked)
keys.add(groupKey(marked))
setExpanded(keys)
}, [open, groups, firstAfterReleaseSha])
useEffect(() => {
if (!open)
@@ -58,7 +47,21 @@ export function CherryPickModal({ open, branch, groups, onCancel, onConfirm }: P
return () => document.removeEventListener('keydown', onKey)
}, [open, onCancel])
const visible = useMemo(() => filterGroups(groups, query), [groups, query])
const visible = useMemo(
() => filterCherryPickGroups(groups, query, enabledAuthors),
[groups, query, enabledAuthors],
)
function toggleAuthor(name: string): void {
setEnabledAuthors((prev) => {
const next = new Set(prev)
if (next.has(name))
next.delete(name)
else
next.add(name)
return next
})
}
if (!open)
return null
@@ -119,13 +122,27 @@ export function CherryPickModal({ open, branch, groups, onCancel, onConfirm }: P
<X className="size-4" />
</button>
</div>
<div className="px-4 pt-3">
<div className="space-y-2 px-4 pt-3">
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="搜索作者、PR 号或标题"
placeholder="搜索 PR 号或标题"
className="w-full rounded border border-[var(--vscode-input-border,transparent)] bg-[var(--vscode-input-background)] px-2 py-1.5 text-xs outline-none"
/>
{authors.length > 0 && (
<div className="flex flex-wrap gap-x-3 gap-y-1">
{authors.map(name => (
<label key={name} className="inline-flex cursor-pointer items-center gap-1 text-xs">
<input
type="checkbox"
checked={enabledAuthors.has(name)}
onChange={() => toggleAuthor(name)}
/>
<span className="opacity-80">{name}</span>
</label>
))}
</div>
)}
</div>
<div className="min-h-0 flex-1 space-y-2 overflow-y-auto px-4 py-3">
{visible.length === 0
@@ -172,7 +189,7 @@ export function CherryPickModal({ open, branch, groups, onCancel, onConfirm }: P
<ul className="border-t border-[var(--vscode-panel-border)] py-1">
{g.commits.map(c => (
<li key={c.sha}>
<label className="flex cursor-pointer items-start gap-2 px-3 py-1.5 hover:bg-white/5">
<label className={`flex cursor-pointer items-start gap-2 px-3 py-1.5 hover:bg-white/5 ${c.sha === firstAfterReleaseSha ? 'bg-[var(--vscode-editor-findMatchHighlightBackground)]' : ''}`}>
<input
type="checkbox"
checked={selected.has(c.sha)}
@@ -183,6 +200,7 @@ export function CherryPickModal({ open, branch, groups, onCancel, onConfirm }: P
<span className="block truncate">{c.subject}</span>
<span className="block text-[11px] opacity-60">
{`${c.sha.slice(0, 7)} · ${c.author} · ${c.date}`}
{c.sha === firstAfterReleaseSha ? ' · 发布点后第一条' : ''}
</span>
</span>
</label>