feat(web): done dot on sessions that finished while unviewed
A session that stops running while it is not the selected session arms a green 'done' reminder dot on its sidebar row, so the operator notices a finished background session and returns to it; opening the session clears the dot, and a re-run re-arms it on completion. SessionManager owns the reminder set (a sibling of the waiting-approval bit): a running->idle edge of a non-selected session arms it, select() consumes it, removal prunes it, and it survives connection generations. The bit rides SessionListEntry/SessionSummary into the workspace browser rows, which render the existing StateDot done state (running keeps the spinner) and label the hover card '已完成/Completed'.
This commit is contained in:
@@ -49,6 +49,7 @@ export const zh = {
|
||||
'status.waitingApproval': '等待审批',
|
||||
'status.planReview': '计划待审',
|
||||
'status.waitingAnswer': '等待回答',
|
||||
'status.completed': '已完成',
|
||||
'hover.created': '创建于 {time}',
|
||||
'hover.copied': '已复制',
|
||||
'date.ymd': '{y}年{m}月{d}日',
|
||||
@@ -109,6 +110,7 @@ export const en = {
|
||||
'status.waitingApproval': 'Waiting for approval',
|
||||
'status.planReview': 'Plan awaiting review',
|
||||
'status.waitingAnswer': 'Waiting for answer',
|
||||
'status.completed': 'Completed',
|
||||
'hover.created': 'Created {time}',
|
||||
'hover.copied': 'Copied',
|
||||
'date.ymd': '{y}-{m}-{d}',
|
||||
|
||||
@@ -173,7 +173,7 @@ function assertNever(value: never): never {
|
||||
|
||||
/** Session status presentation; pending user interaction outranks the running state. */
|
||||
function sessionStatus(
|
||||
node: Pick<SessionNode, 'pendingInteraction' | 'running'>,
|
||||
node: Pick<SessionNode, 'pendingInteraction' | 'running' | 'completed'>,
|
||||
t: RowTranslate,
|
||||
): { state: StateDotState; label: string } {
|
||||
switch (node.pendingInteraction) {
|
||||
@@ -185,10 +185,11 @@ function sessionStatus(
|
||||
default: return assertNever(node.pendingInteraction)
|
||||
}
|
||||
if (node.running) return { state: 'ongoing', label: t('status.running') }
|
||||
if (node.completed) return { state: 'done', label: t('status.completed') }
|
||||
return { state: 'done', label: t('status.idle') }
|
||||
}
|
||||
|
||||
/** Hover-card body: full title, relative time, and interaction/running/idle status. */
|
||||
/** Hover-card body: full title, relative time, and interaction/running/completed/idle status. */
|
||||
function SessionHoverContent({ node, now, t }: { node: SessionNode; now: number; t: RowTranslate }) {
|
||||
const status = sessionStatus(node, t)
|
||||
return (
|
||||
@@ -251,7 +252,7 @@ export function SearchResultItem({ result, currentId, onOpen, t }: {
|
||||
>
|
||||
<span className={css.searchResultHeading}>
|
||||
<span className={css.slot}>
|
||||
{status.state !== 'done' && (
|
||||
{(status.state !== 'done' || result.completed) && (
|
||||
<>
|
||||
<StateDot state={status.state} />
|
||||
<span className={css.visuallyHidden}>{status.label}</span>
|
||||
@@ -351,8 +352,11 @@ export function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork
|
||||
drag.drop(rowHalf(e))
|
||||
}}
|
||||
>
|
||||
{/* Pending interactions and running outrank the idle state; a
|
||||
finished-but-unviewed session shows the green done reminder dot
|
||||
(cleared by opening the session). */}
|
||||
<span className={css.slot}>
|
||||
{status.state !== 'done' && (
|
||||
{(status.state !== 'done' || row.completed) && (
|
||||
<>
|
||||
<StateDot state={status.state} />
|
||||
<span className={css.visuallyHidden}>{status.label}</span>
|
||||
|
||||
@@ -24,6 +24,8 @@ export interface SessionNode {
|
||||
/** The runtime Session list reports an interaction awaiting this user. */
|
||||
pendingInteraction?: PendingInteractionStatus
|
||||
running: boolean
|
||||
/** Finished running while not selected and not yet opened (the green "done" reminder dot). */
|
||||
completed: boolean
|
||||
updatedAt: number
|
||||
}
|
||||
|
||||
@@ -54,6 +56,8 @@ export interface SearchResultNode {
|
||||
/** The runtime Session list reports an interaction awaiting this user. */
|
||||
pendingInteraction?: PendingInteractionStatus
|
||||
running: boolean
|
||||
/** Finished running while not selected and not yet opened (the green "done" reminder dot). */
|
||||
completed: boolean
|
||||
snippet?: string
|
||||
}
|
||||
|
||||
@@ -175,6 +179,7 @@ function sessionNode(s: SessionSummary): SessionNode {
|
||||
title: sessionTitle(s),
|
||||
blank: s.blank,
|
||||
running: s.running,
|
||||
completed: s.completed === true,
|
||||
updatedAt: s.updatedAt,
|
||||
...(s.pendingInteraction === undefined ? {} : { pendingInteraction: s.pendingInteraction }),
|
||||
}
|
||||
@@ -330,6 +335,7 @@ export function deriveSearchResults(
|
||||
...(summary.pendingInteraction === undefined
|
||||
? {}
|
||||
: { pendingInteraction: summary.pendingInteraction }),
|
||||
completed: summary.completed === true,
|
||||
...match === undefined ? {} : { snippet: match.snippet },
|
||||
}
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user