🐛 fix(vscode): 别的机器上开的会话在本机没有会话文件时,会话 id 行置灰不可双击,不再 resume 报错
This commit is contained in:
1 parent
2b1e660cc8
commit
bf4661e364
20 files changed
+508
-234
No files matched your search
@@ -5,11 +5,15 @@ import { ArrowRightLeft, CircleSlash, Download, Eraser, ExternalLink, GitMerge,
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { isIssueLocked } from '../lib/dependencies'
|
||||
import { isIssueMine, issueOwnerLogin } from '../lib/issueOwnership'
|
||||
import { isSessionMissing } from '../lib/missingSession'
|
||||
import { toReadOnlySchema } from '../lib/readOnlySchema'
|
||||
import { COLUMN_LABELS, COLUMN_ORDER } from '../types'
|
||||
import { PropertyGrid } from './property-grid'
|
||||
import { Tooltip } from './ui/tooltip'
|
||||
|
||||
/** 会话 id 在共享状态里、本机却没有会话文件时,各会话行的统一说明。 */
|
||||
const MISSING_SESSION_DESC = '本机没有该会话文件(会话在别的机器上创建),无法 resume'
|
||||
|
||||
/** 跨机绝对路径只比 basename(同名 .json)映射到本机 profiles 列表。 */
|
||||
function matchProfileOption(
|
||||
current: string | undefined,
|
||||
@@ -194,10 +198,10 @@ export function IssueDetailPanel({
|
||||
: undefined
|
||||
const prDiffSummaryRunning = issue ? isPrDiffSummaryRunning(issue.number) : false
|
||||
|
||||
// 三个会话 id 行(头脑风暴/实施/审查)共用一个"上次触发时间戳"表,
|
||||
// 各会话 id 行(头脑风暴/实施/审查/测试/原型)共用一个"上次触发时间戳"表,
|
||||
// 防止用户连续快速双击同一行时触发多次 resume —— extension 端 findExistingTerminal
|
||||
// 在前一个 createTerminal 尚未完成时查不到已存在的终端,会重复开 tab。
|
||||
// key 用 sessionId 字符串本身(足够区分三种行 + 不同工单)。
|
||||
// key 用 sessionId 字符串本身(足够区分不同行 + 不同工单)。
|
||||
const lastFiredAtRef = useRef<Map<string, number>>(new Map())
|
||||
const RESUME_THROTTLE_MS = 800
|
||||
const throttleBySessionId = (sid: string): boolean => {
|
||||
@@ -242,9 +246,12 @@ export function IssueDetailPanel({
|
||||
// - 工单未完成 → 一直有 ▶ 续聊。插件把续聊提示词送进对应会话:
|
||||
// 规划终端开着就直接打进去,关了就 --resume 带提示词,没会话就新开。
|
||||
// - brainstormTabOpen → 再加一个 X 关闭 tab
|
||||
// - 会话 id 本机没有会话文件 → 只读不可双击,续聊会新开会话
|
||||
const hasSession = !!(issue?.sessionId && issue.sessionId.length > 0)
|
||||
const missing = isSessionMissing(issue, issue?.sessionId)
|
||||
const canContinue = issue?.column !== 'done'
|
||||
const secondaryActions: RowAction[] = []
|
||||
if (issue?.column !== 'done') {
|
||||
if (canContinue) {
|
||||
secondaryActions.push({
|
||||
icon: <Play className="size-3.5" />,
|
||||
title: '续聊:把续聊提示词发给头脑风暴会话',
|
||||
@@ -274,6 +281,17 @@ export function IssueDetailPanel({
|
||||
},
|
||||
})
|
||||
}
|
||||
if (missing) {
|
||||
return {
|
||||
key: 'sessionId',
|
||||
label: '头脑风暴会话id',
|
||||
type: 'action' as const,
|
||||
readOnly: true,
|
||||
description: canContinue ? `${MISSING_SESSION_DESC};点 ▶ 续聊会新开一个规划会话` : MISSING_SESSION_DESC,
|
||||
actionIcon: <Terminal className="size-3.5" />,
|
||||
secondaryActions,
|
||||
}
|
||||
}
|
||||
return {
|
||||
key: 'sessionId',
|
||||
label: '头脑风暴会话id',
|
||||
@@ -293,27 +311,14 @@ export function IssueDetailPanel({
|
||||
secondaryActions,
|
||||
}
|
||||
})(),
|
||||
// 实施会话即使 worktree 已被清掉也保留可双击的 resume 入口:
|
||||
// extension 端 handleResumeSession 会在 worktree 路径不存在时退回
|
||||
// 工作区根目录,并通过 toast 告知用户;description 在两种状态下
|
||||
// 给出不同的提示文案。
|
||||
{
|
||||
key: 'implementSessionId',
|
||||
label: '实施会话id',
|
||||
type: 'action',
|
||||
pending: issue?.implementSessionId ? isSessionTabPending(issue.implementSessionId) : false,
|
||||
description: issue?.worktreeExists
|
||||
? '双击在新终端运行 claude --resume <id> 恢复实施对话(cwd 为 worktree)'
|
||||
: 'worktree 已清理,将在工作区根目录恢复(cc 可能提示原 cwd 不存在)',
|
||||
actionIcon: <Terminal className="size-3.5" />,
|
||||
onAction: (v) => {
|
||||
if (typeof v !== 'string' || v.length === 0)
|
||||
return
|
||||
if (!throttleBySessionId(v))
|
||||
return
|
||||
onResumeSession(v, 'implement', issue?.profilePath, issue?.worktreePath, issue?.number)
|
||||
},
|
||||
secondaryActions: (() => {
|
||||
(() => {
|
||||
// 实施会话即使 worktree 已被清掉也保留可双击的 resume 入口:
|
||||
// extension 端 handleResumeSession 会在 worktree 路径不存在时退回
|
||||
// 工作区根目录,并通过 toast 告知用户;description 在两种状态下
|
||||
// 给出不同的提示文案。
|
||||
// 本机没有会话文件时只读且不给 ▶:重新实施会先删远程 feature 分支,
|
||||
// 会把别的机器上的实施进度一并抹掉。
|
||||
const secondaryActions: RowAction[] = (() => {
|
||||
if (issue?.implementTabOpen) {
|
||||
return [{
|
||||
icon: <X className="size-3.5" />,
|
||||
@@ -333,9 +338,38 @@ export function IssueDetailPanel({
|
||||
|| locked,
|
||||
onClick: () => onImplement(issue.number, issue.planFile ?? '', issue.profilePath, issue.sessionId),
|
||||
}]
|
||||
})(),
|
||||
},
|
||||
issue?.reviewSessionFileExists
|
||||
})()
|
||||
if (isSessionMissing(issue, issue?.implementSessionId)) {
|
||||
return {
|
||||
key: 'implementSessionId',
|
||||
label: '实施会话id',
|
||||
type: 'action' as const,
|
||||
readOnly: true,
|
||||
description: `${MISSING_SESSION_DESC};要在本机接着实施,请让对方把工单移交过来`,
|
||||
actionIcon: <Terminal className="size-3.5" />,
|
||||
secondaryActions,
|
||||
}
|
||||
}
|
||||
return {
|
||||
key: 'implementSessionId',
|
||||
label: '实施会话id',
|
||||
type: 'action' as const,
|
||||
pending: issue?.implementSessionId ? isSessionTabPending(issue.implementSessionId) : false,
|
||||
description: issue?.worktreeExists
|
||||
? '双击在新终端运行 claude --resume <id> 恢复实施对话(cwd 为 worktree)'
|
||||
: 'worktree 已清理,将在工作区根目录恢复(cc 可能提示原 cwd 不存在)',
|
||||
actionIcon: <Terminal className="size-3.5" />,
|
||||
onAction: (v: unknown) => {
|
||||
if (typeof v !== 'string' || v.length === 0)
|
||||
return
|
||||
if (!throttleBySessionId(v))
|
||||
return
|
||||
onResumeSession(v, 'implement', issue?.profilePath, issue?.worktreePath, issue?.number)
|
||||
},
|
||||
secondaryActions,
|
||||
}
|
||||
})(),
|
||||
issue?.reviewSessionId && !isSessionMissing(issue, issue.reviewSessionId)
|
||||
? {
|
||||
key: 'reviewSessionId',
|
||||
label: '审查会话id',
|
||||
@@ -364,7 +398,7 @@ export function IssueDetailPanel({
|
||||
type: 'action',
|
||||
readOnly: true,
|
||||
description: issue?.reviewSessionId
|
||||
? '本机没有该 codex 会话文件,无法 resume;点 ▶ 对当前 PR 重新起一个审查会话'
|
||||
? `${MISSING_SESSION_DESC};点 ▶ 对当前 PR 重新起一个审查会话`
|
||||
: '还没有审查会话;点 ▶ 对当前 PR 启动 codex 审查(等同 PR 事件自动触发的那次)',
|
||||
secondaryActions: issue?.pr
|
||||
? [{
|
||||
@@ -383,8 +417,11 @@ export function IssueDetailPanel({
|
||||
// 条件与 handleStartTestSession 一致:worktree 还在就进 worktree 测,
|
||||
// 代码本就在工作目录,不需要 PR;worktree 已清理才回退主工作区,
|
||||
// 那条路径要靠 PR 让 cc 了解合并进来的改动。
|
||||
// 会话 id 本机没有会话文件 → 只读不可双击,按状态 3 的条件给 ▶ 重新起一个
|
||||
// (启动会覆盖旧 testSessionId)。
|
||||
const hasSession = !!(issue?.testSessionId && issue.testSessionId.length > 0)
|
||||
const canStart = !hasSession && (!!issue?.worktreeExists || !!issue?.pr)
|
||||
const missing = isSessionMissing(issue, issue?.testSessionId)
|
||||
const canStart = (!hasSession || missing) && (!!issue?.worktreeExists || !!issue?.pr)
|
||||
const secondaryActions: RowAction[] = []
|
||||
if (hasSession && issue?.testTabOpen) {
|
||||
secondaryActions.push({
|
||||
@@ -406,6 +443,17 @@ export function IssueDetailPanel({
|
||||
},
|
||||
})
|
||||
}
|
||||
if (missing) {
|
||||
return {
|
||||
key: 'testSessionId',
|
||||
label: '测试会话id',
|
||||
type: 'action' as const,
|
||||
readOnly: true,
|
||||
description: `${MISSING_SESSION_DESC};点 ▶ 新起一个测试会话(worktree 还在就在 worktree 里测,已清理则需要工单有已合并的 PR)`,
|
||||
actionIcon: <Terminal className="size-3.5" />,
|
||||
secondaryActions,
|
||||
}
|
||||
}
|
||||
return {
|
||||
key: 'testSessionId',
|
||||
label: '测试会话id',
|
||||
@@ -428,7 +476,10 @@ export function IssueDetailPanel({
|
||||
(() => {
|
||||
// 原型会话 id 行:与测试行同构的三态,差别是启动不看 PR——原型只照
|
||||
// 工单正文画界面,任何阶段都能起;前置工单没做完时按钮禁用。
|
||||
// 会话 id 本机没有会话文件 → 只读不可双击,同样给 ▶ 重新起一个
|
||||
// (启动会覆盖旧 prototypeSessionId)。
|
||||
const hasSession = !!(issue?.prototypeSessionId && issue.prototypeSessionId.length > 0)
|
||||
const missing = isSessionMissing(issue, issue?.prototypeSessionId)
|
||||
const secondaryActions: RowAction[] = []
|
||||
if (hasSession && issue?.prototypeTabOpen) {
|
||||
secondaryActions.push({
|
||||
@@ -440,7 +491,7 @@ export function IssueDetailPanel({
|
||||
},
|
||||
})
|
||||
}
|
||||
else if (!hasSession) {
|
||||
else if (!hasSession || missing) {
|
||||
secondaryActions.push({
|
||||
icon: <Play className="size-3.5" />,
|
||||
title: locked ? lockTitle : '启动原型会话',
|
||||
@@ -451,6 +502,17 @@ export function IssueDetailPanel({
|
||||
},
|
||||
})
|
||||
}
|
||||
if (missing) {
|
||||
return {
|
||||
key: 'prototypeSessionId',
|
||||
label: '原型会话id',
|
||||
type: 'action' as const,
|
||||
readOnly: true,
|
||||
description: `${MISSING_SESSION_DESC};点 ▶ 新起一个原型会话`,
|
||||
actionIcon: <Terminal className="size-3.5" />,
|
||||
secondaryActions,
|
||||
}
|
||||
}
|
||||
return {
|
||||
key: 'prototypeSessionId',
|
||||
label: '原型会话id',
|
||||
|
||||
Reference in new issue
Block a user