diff --git a/packages/client/ui-workspace/src/client/rows/Rows.tsx b/packages/client/ui-workspace/src/client/rows/Rows.tsx
index a8b28f17e4..77583140c1 100644
--- a/packages/client/ui-workspace/src/client/rows/Rows.tsx
+++ b/packages/client/ui-workspace/src/client/rows/Rows.tsx
@@ -175,7 +175,9 @@ function SessionHoverContent({ node, now, t }: { node: SessionNode; now: number;
return (
{displayTitle(node, t)}
-
{hoverTimeLabel(node.updatedAt, now, t)}
+ {/* Same placeholder rule as the row's trailing cell: no timestamp
+ before the first prompt. */}
+ {!node.blank &&
{hoverTimeLabel(node.updatedAt, now, t)}
}
{node.running ? t('status.running') : t('status.idle')}
@@ -306,32 +308,38 @@ export function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork
>
{row.running && }{title}
- {timeLabel(row.updatedAt, now, t)}
-
-
+ {/* A blank New Session row is a provisional placeholder: nothing has
+ happened in it yet, so a "now" timestamp and the row verbs
+ (rename/fork/archive) would all act on content that does not
+ exist — both trailing cells stay off until the first prompt. */}
+ {!row.blank && {timeLabel(row.updatedAt, now, t)}}
+ {!row.blank && (
+
+
+ )}
)
return (
diff --git a/packages/client/ui-workspace/tests/rows.spec.tsx b/packages/client/ui-workspace/tests/rows.spec.tsx
index ee45b8628d..c0b4959b17 100644
--- a/packages/client/ui-workspace/tests/rows.spec.tsx
+++ b/packages/client/ui-workspace/tests/rows.spec.tsx
@@ -157,6 +157,29 @@ describe('workspace browser rows', () => {
expect(screen.queryByRole('button', { name: /工作区/ })).toBeNull()
})
+ it('blank New Session rows carry no menu, no time label, and no hover-card time', () => {
+ vi.useFakeTimers()
+ try {
+ const node: SessionNode = {
+ id: sid('s-blank'), title: 'ignored', blank: true, running: false, updatedAt: 0,
+ }
+ render()
+ // The placeholder has no content yet: no row verbs, no "now" stamp.
+ expect(screen.queryByRole('button', { name: /会话.*的操作/ })).toBeNull()
+ expect(screen.queryByText('刚刚')).toBeNull()
+ // The hover card keeps title + status but drops the timestamp line.
+ const wrapper = screen.getByRole('treeitem').parentElement as HTMLElement
+ fireEvent.pointerEnter(wrapper)
+ act(() => { vi.advanceTimersByTime(500) })
+ expect(screen.getAllByText('新会话').length).toBeGreaterThanOrEqual(2)
+ expect(screen.getByText('空闲')).toBeTruthy()
+ expect(screen.queryByText('刚刚')).toBeNull()
+ } finally {
+ vi.useRealTimers()
+ }
+ })
+
it('session row menu opens without opening the session and dispatches rename, fork, and archive', () => {
const onOpen = vi.fn()
const onRename = vi.fn()