✨ feat(vscode): 会话 tab 可交接对话给同事
This commit is contained in:
1 parent
5ee8226167
commit
77037924ef
20 files changed
+938
-26
No files matched your search
@@ -8,8 +8,9 @@
|
||||
*/
|
||||
|
||||
import type { ClaudeProfile } from '../hooks/useIssues'
|
||||
import type { SessionHandoffPendingItem } from '../lib/messages'
|
||||
import type { ManagedSession, ManagedSessionsData } from '../types'
|
||||
import { Download, Plus, Terminal, Trash2, X } from 'lucide-react'
|
||||
import { Download, Plus, Terminal, Trash2, UserRoundPlus, X } from 'lucide-react'
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { SelectMenu } from './ui/select-menu'
|
||||
@@ -26,6 +27,9 @@ interface ManagedSessionsPanelProps {
|
||||
importableLoading: boolean
|
||||
onListImportable: () => void
|
||||
onImport: (sessionId: string, name: string) => void
|
||||
pendingHandoffs: SessionHandoffPendingItem[]
|
||||
onHandoff: (sessionId: string) => void
|
||||
onAcceptHandoff: (attachmentId: number) => void
|
||||
}
|
||||
|
||||
function basename(p?: string): string {
|
||||
@@ -73,6 +77,9 @@ export function ManagedSessionsPanel({
|
||||
importableLoading,
|
||||
onListImportable,
|
||||
onImport,
|
||||
pendingHandoffs,
|
||||
onHandoff,
|
||||
onAcceptHandoff,
|
||||
}: ManagedSessionsPanelProps) {
|
||||
const [selectedProfile, setSelectedProfile] = useState<string>('')
|
||||
const [name, setName] = useState<string>('')
|
||||
@@ -242,6 +249,33 @@ export function ManagedSessionsPanel({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{pendingHandoffs.length > 0 && (
|
||||
<div className="shrink-0 border-b border-[var(--vscode-panel-border)] p-2">
|
||||
<div className="mb-1 text-[10px] text-[var(--vscode-descriptionForeground)]">待接收</div>
|
||||
<ul className="flex flex-col gap-1">
|
||||
{pendingHandoffs.map(p => (
|
||||
<li key={p.attachmentId} className="flex items-center gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-xs">{p.name || p.sid.slice(0, 8)}</div>
|
||||
<div className="truncate text-[10px] text-[var(--vscode-descriptionForeground)]">
|
||||
来自
|
||||
{' '}
|
||||
{p.from}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onAcceptHandoff(p.attachmentId)}
|
||||
className="shrink-0 rounded bg-[var(--vscode-button-background)] px-2 py-1 text-xs text-[var(--vscode-button-foreground)] hover:bg-[var(--vscode-button-hoverBackground)]"
|
||||
>
|
||||
接收
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 下方会话列表 */}
|
||||
<div className="min-h-0 flex-1 overflow-auto">
|
||||
{sessions.length === 0
|
||||
@@ -261,6 +295,7 @@ export function ManagedSessionsPanel({
|
||||
onResume={onResume}
|
||||
onDelete={onDelete}
|
||||
onCloseTab={onCloseTab}
|
||||
onHandoff={onHandoff}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
@@ -277,9 +312,10 @@ interface SessionRowProps {
|
||||
onResume: (id: string, profilePath?: string) => void
|
||||
onDelete: (id: string) => void
|
||||
onCloseTab: (id: string) => void
|
||||
onHandoff: (id: string) => void
|
||||
}
|
||||
|
||||
function SessionRow({ session, profiles, onRename, onResume, onDelete, onCloseTab }: SessionRowProps) {
|
||||
function SessionRow({ session, profiles, onRename, onResume, onDelete, onCloseTab, onHandoff }: SessionRowProps) {
|
||||
const [editing, setEditing] = useState(false)
|
||||
const [draft, setDraft] = useState(session.name)
|
||||
const [userPicked, setUserPicked] = useState<string | undefined>(undefined)
|
||||
@@ -398,12 +434,41 @@ function SessionRow({ session, profiles, onRename, onResume, onDelete, onCloseTa
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{created ? <span className="shrink-0">{(profiles.length > 0 || Boolean(basename(session.profilePath))) ? ' · ' : ''}{created}</span> : null}
|
||||
{created
|
||||
? (
|
||||
<span className="shrink-0">
|
||||
{(profiles.length > 0 || Boolean(basename(session.profilePath))) ? ' · ' : ''}
|
||||
{created}
|
||||
</span>
|
||||
)
|
||||
: null}
|
||||
{session.handedOffTo
|
||||
? (
|
||||
<span className="ml-1 shrink-0 text-[10px] text-[var(--vscode-descriptionForeground)]">
|
||||
· 已交
|
||||
{' '}
|
||||
{session.handedOffTo}
|
||||
</span>
|
||||
)
|
||||
: null}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onHandoff(session.id)
|
||||
}}
|
||||
title="交接给同事"
|
||||
aria-label="交接给同事"
|
||||
className="grid size-6 shrink-0 place-items-center rounded text-[var(--vscode-descriptionForeground)] opacity-0 transition-opacity hover:bg-[var(--vscode-toolbar-hoverBackground,var(--vscode-list-hoverBackground))] hover:text-[var(--vscode-foreground)] group-hover:opacity-70"
|
||||
>
|
||||
<UserRoundPlus className="size-3.5" />
|
||||
</button>
|
||||
|
||||
{session.tabOpen && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in new issue
Block a user