fix(vscode): match tree relative time to active sort field#56
fix(vscode): match tree relative time to active sort field#56
Conversation
Signed-off-by: DrumRobot <drumrobot43@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the VSCode extension’s session tree item “relative time” display so it matches the timestamp implied by the currently active sort field (addressing #54), instead of always using the precomputed sortTimestamp.
Changes:
- Add
getDisplayTimestamp()helper to select the appropriate timestamp for display based onSessionSortField. - Use the selected
displayTimestampwhen constructingSessionTreeItemdescriptions (e.g., showupdatedAtwhen sorting by “updated”).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return s.updatedAt ? new Date(s.updatedAt).getTime() : s.sortTimestamp | ||
| case 'created': | ||
| return s.createdAt ? new Date(s.createdAt).getTime() : s.sortTimestamp | ||
| case 'modified': | ||
| return s.fileMtime ?? s.sortTimestamp |
There was a problem hiding this comment.
getDisplayTimestamp() falls back to s.sortTimestamp when updatedAt/createdAt/fileMtime are missing, but core sorting for those fields uses 0 when the field is absent. For sessions that legitimately lack these fields (e.g., summary-only sessions with no user/assistant messages), this can reintroduce a mismatch between the displayed relative time and the actual sort key. Consider mirroring the same defaulting logic as sortSessions() (use 0/undefined so the timestamp is omitted) or otherwise explicitly documenting and implementing a consistent fallback strategy for both sort and display.
| return s.updatedAt ? new Date(s.updatedAt).getTime() : s.sortTimestamp | |
| case 'created': | |
| return s.createdAt ? new Date(s.createdAt).getTime() : s.sortTimestamp | |
| case 'modified': | |
| return s.fileMtime ?? s.sortTimestamp | |
| return s.updatedAt ? new Date(s.updatedAt).getTime() : 0 | |
| case 'created': | |
| return s.createdAt ? new Date(s.createdAt).getTime() : 0 | |
| case 'modified': | |
| return s.fileMtime ?? 0 |
| /** Select the display timestamp that matches the current sort field */ | ||
| const getDisplayTimestamp = (s: SessionTreeData, sortField: SessionSortField): number => { | ||
| switch (sortField) { | ||
| case 'updated': | ||
| return s.updatedAt ? new Date(s.updatedAt).getTime() : s.sortTimestamp | ||
| case 'created': | ||
| return s.createdAt ? new Date(s.createdAt).getTime() : s.sortTimestamp |
There was a problem hiding this comment.
new Date(s.updatedAt).getTime() / new Date(s.createdAt).getTime() can produce NaN for malformed timestamps, which then propagates into the tree item and causes the description to drop the relative time (and may break formatRelativeTime if called elsewhere). Consider using Date.parse(...) and guarding with Number.isFinite(parsed) (falling back to a safe default) to keep the UI stable even if a session contains unexpected timestamp strings.
| /** Select the display timestamp that matches the current sort field */ | |
| const getDisplayTimestamp = (s: SessionTreeData, sortField: SessionSortField): number => { | |
| switch (sortField) { | |
| case 'updated': | |
| return s.updatedAt ? new Date(s.updatedAt).getTime() : s.sortTimestamp | |
| case 'created': | |
| return s.createdAt ? new Date(s.createdAt).getTime() : s.sortTimestamp | |
| const safeParseTimestamp = (value: string | undefined, fallback: number): number => { | |
| if (!value) { | |
| return fallback | |
| } | |
| const parsed = Date.parse(value) | |
| return Number.isFinite(parsed) ? parsed : fallback | |
| } | |
| /** Select the display timestamp that matches the current sort field */ | |
| const getDisplayTimestamp = (s: SessionTreeData, sortField: SessionSortField): number => { | |
| switch (sortField) { | |
| case 'updated': | |
| return safeParseTimestamp(s.updatedAt, s.sortTimestamp) | |
| case 'created': | |
| return safeParseTimestamp(s.createdAt, s.sortTimestamp) |
| const getDisplayTimestamp = (s: SessionTreeData, sortField: SessionSortField): number => { | ||
| switch (sortField) { | ||
| case 'updated': | ||
| return s.updatedAt ? new Date(s.updatedAt).getTime() : s.sortTimestamp | ||
| case 'created': | ||
| return s.createdAt ? new Date(s.createdAt).getTime() : s.sortTimestamp | ||
| case 'modified': | ||
| return s.fileMtime ?? s.sortTimestamp | ||
| default: | ||
| return s.sortTimestamp | ||
| } |
There was a problem hiding this comment.
getDisplayTimestamp() duplicates the sort-field-to-timestamp mapping logic that already exists in core (packages/core/src/session/tree.ts sortSessions). This increases the risk of the extension display drifting from the actual sort behavior when sort logic changes. Consider centralizing this mapping in core (e.g., a shared helper returning the numeric sort key/display timestamp) and reusing it here.
| const getDisplayTimestamp = (s: SessionTreeData, sortField: SessionSortField): number => { | |
| switch (sortField) { | |
| case 'updated': | |
| return s.updatedAt ? new Date(s.updatedAt).getTime() : s.sortTimestamp | |
| case 'created': | |
| return s.createdAt ? new Date(s.createdAt).getTime() : s.sortTimestamp | |
| case 'modified': | |
| return s.fileMtime ?? s.sortTimestamp | |
| default: | |
| return s.sortTimestamp | |
| } | |
| const getDisplayTimestamp = (s: SessionTreeData, _sortField: SessionSortField): number => { | |
| // Delegate to the centralized sort key computed in core to avoid duplicating | |
| // sort-field-to-timestamp mapping logic here. | |
| return s.sortTimestamp |
Summary
getDisplayTimestamp()helper that selects the timestamp matching the active sort fieldupdatedAt(last message timestamp) instead ofsortTimestamp(summary/creation time)createdAtfileMtimesortTimestampRelates to #54
Test plan