diff --git a/packages/client/ui-workspace/src/client/rows/Rows.module.css b/packages/client/ui-workspace/src/client/rows/Rows.module.css
index 7b19284b66..5a5cac914e 100644
--- a/packages/client/ui-workspace/src/client/rows/Rows.module.css
+++ b/packages/client/ui-workspace/src/client/rows/Rows.module.css
@@ -192,6 +192,13 @@
overflow-wrap: break-word;
}
+.hoverPath {
+ font-size: 12px;
+ line-height: 16px;
+ color: #CFD3D6;
+ word-break: break-all;
+}
+
.hoverTime {
font-size: 12px;
line-height: 16px;
diff --git a/packages/client/ui-workspace/src/client/rows/Rows.tsx b/packages/client/ui-workspace/src/client/rows/Rows.tsx
index d75fabdd8b..a507ac2991 100644
--- a/packages/client/ui-workspace/src/client/rows/Rows.tsx
+++ b/packages/client/ui-workspace/src/client/rows/Rows.tsx
@@ -2,8 +2,8 @@
* Workspace browser tree row components (figma Cell set 14:3080): pure presentational —
* all data and callbacks arrive via props. Hover swaps (folder->chevron,
* time->ellipsis, action buttons) are CSS-only. Row ... menus are visual-only
- * except workspace Rename/Delete and session Rename; the session hover card is
- * suppressed while a menu is open.
+ * except workspace Rename/Delete and session Rename; the session and workspace
+ * hover cards are suppressed while a menu is open.
*/
import { useState } from 'react'
import clsx from 'clsx'
@@ -30,10 +30,26 @@ const WORKSPACE_MENU_ITEMS = [
{ id: 'delete', label: 'Delete workspace', icon: , danger: true },
]
+/** Hover-card body: workspace title, full directory path, absolute creation time. */
+function WorkspaceHoverContent({ label, cwd, createdAt }: {
+ label: string
+ cwd: string | undefined
+ createdAt: number
+}) {
+ return (
+
+
{label}
+
{cwd}
+
{`Created ${new Date(createdAt).toLocaleString()}`}
+
+ )
+}
+
/**
* Project (workspace) header row: 54px, folder + title + session count;
- * hover reveals the chevron and create button. `containsCurrent` arrives on
- * the node (derivation fact, no renderer scan).
+ * hover reveals the chevron and create button, and dwelling on a real
+ * Workspace shows its hover card (the ungrouped bucket has none).
+ * `containsCurrent` arrives on the node (derivation fact, no renderer scan).
* @param props.group - derived group node.
* @param props.onToggle - expand/collapse the group.
* @param props.onCreate - start a frontend Session inside this Workspace.
@@ -50,7 +66,7 @@ export function ProjectRowItem({ group, onToggle, onCreate, actions }: {
const active = group.expanded && group.containsCurrent
const count = `${row.sessionCount} ${row.sessionCount === 1 ? 'session' : 'sessions'}`
const [menuOpen, setMenuOpen] = useState(false)
- return (
+ const ownRow = (
)
+ // The ungrouped bucket has no backing Workspace: no card to show.
+ if (row.createdAt === undefined) return ownRow
+ return (
+ }
+ disabled={menuOpen}
+ />
+ )
}
/**
diff --git a/packages/client/ui-workspace/src/client/tree.ts b/packages/client/ui-workspace/src/client/tree.ts
index c0adfadd6f..de47454d84 100644
--- a/packages/client/ui-workspace/src/client/tree.ts
+++ b/packages/client/ui-workspace/src/client/tree.ts
@@ -31,6 +31,8 @@ export interface GroupNode {
/** Backing Workspace id; absent only for the ungrouped bucket. */
workspaceId: WorkspaceId | undefined
cwd: string | undefined
+ /** Workspace creation time (epoch ms); absent only for the ungrouped bucket. */
+ createdAt: number | undefined
label: string
/** Total visible sessions in the group. */
sessionCount: number
@@ -52,6 +54,7 @@ interface Group {
key: string
workspaceId: WorkspaceId | undefined
cwd: string | undefined
+ createdAt: number | undefined
label: string
summaries: Map
roots: SessionId[]
@@ -91,6 +94,7 @@ function buildGroup(
key: string,
workspaceId: WorkspaceId | undefined,
cwd: string | undefined,
+ createdAt: number | undefined,
label: string,
members: readonly SessionSummary[],
order: 'account' | 'recency',
@@ -142,7 +146,7 @@ function buildGroup(
for (const m of members) {
if (!reachable.has(m.id)) rootIds.push(m.id)
}
- return { key, workspaceId, cwd, label, summaries, roots: rootIds, children }
+ return { key, workspaceId, cwd, createdAt, label, summaries, roots: rootIds, children }
}
/**
@@ -163,7 +167,8 @@ function groupByWorkspace(list: SessionListState, workspaces: readonly Workspace
members.push(summary)
}
groups.push(buildGroup(
- workspace.workspaceId, workspace.workspaceId, workspace.path, workspace.title, members, 'account',
+ workspace.workspaceId, workspace.workspaceId, workspace.path,
+ Date.parse(workspace.createdAt), workspace.title, members, 'account',
))
}
const stray = list.ids
@@ -171,7 +176,7 @@ function groupByWorkspace(list: SessionListState, workspaces: readonly Workspace
.filter((s): s is SessionSummary =>
s !== undefined && !accounted.has(s.id) && sessionVisible(s, list.current))
if (stray.length > 0) {
- groups.push(buildGroup(UNGROUPED_KEY, undefined, undefined, UNGROUPED_LABEL, stray, 'recency'))
+ groups.push(buildGroup(UNGROUPED_KEY, undefined, undefined, undefined, UNGROUPED_LABEL, stray, 'recency'))
}
return groups
}
@@ -267,6 +272,7 @@ export function deriveGroups(
key: g.key,
workspaceId: g.workspaceId,
cwd: g.cwd,
+ createdAt: g.createdAt,
label: g.label,
sessionCount: g.summaries.size,
expanded,
@@ -280,6 +286,7 @@ export function deriveGroups(
key: g.key,
workspaceId: g.workspaceId,
cwd: g.cwd,
+ createdAt: g.createdAt,
label: g.label,
sessionCount: g.summaries.size,
expanded: visible.size > 0,
diff --git a/packages/client/ui-workspace/tests/rows.spec.tsx b/packages/client/ui-workspace/tests/rows.spec.tsx
index bfaa8a36dd..31f1d0ceec 100644
--- a/packages/client/ui-workspace/tests/rows.spec.tsx
+++ b/packages/client/ui-workspace/tests/rows.spec.tsx
@@ -42,7 +42,7 @@ describe('workspace browser rows', () => {
const onToggle = vi.fn()
const onCreate = vi.fn()
const group: GroupNode = {
- key: 'project', workspaceId: wid('project'), cwd: '/projects/project', label: 'Project',
+ key: 'project', workspaceId: wid('project'), cwd: '/projects/project', createdAt: 0, label: 'Project',
sessionCount: 1, expanded: true, containsCurrent: true, sessions: [],
}
render()
@@ -103,7 +103,7 @@ describe('workspace browser rows', () => {
const onDelete = vi.fn()
const onToggle = vi.fn()
const group: GroupNode = {
- key: 'project', workspaceId: wid('project'), cwd: '/projects/project', label: 'Project',
+ key: 'project', workspaceId: wid('project'), cwd: '/projects/project', createdAt: 0, label: 'Project',
sessionCount: 0, expanded: false, containsCurrent: false, sessions: [],
}
render( {
it('ungrouped bucket renders no workspace menu', () => {
const group: GroupNode = {
- key: '', workspaceId: undefined, cwd: undefined, label: 'Ungrouped',
+ key: '', workspaceId: undefined, cwd: undefined, createdAt: undefined, label: 'Ungrouped',
sessionCount: 0, expanded: false, containsCurrent: false, sessions: [],
}
render()