Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions packages/cli/src/commands/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
type Turn,
type UndoMode,
} from "../utils/historyOwner.js";
import { ago } from "../utils/historyAgo.js";
import { AmbiguousPreviewServerError } from "../utils/studioSelectionClient.js";
import { withMeta } from "../utils/updateCheck.js";

Expand Down Expand Up @@ -103,20 +104,12 @@ function since(entries: HistoryListItem[], ref: string, who: HistoryWho): Histor
return entries.slice(entries.indexOf(entryOf(entries, ref)) + 1);
}

function ago(at: number): string {
const seconds = Math.max(0, Math.round((historyDeps.now() - at) / 1000));
if (seconds < 60) return `${seconds}s ago`;
if (seconds < 3600) return `${Math.round(seconds / 60)}m ago`;
if (seconds < 86_400) return `${Math.round(seconds / 3600)}h ago`;
return `${Math.round(seconds / 86_400)}d ago`;
}

function line(entry: HistoryListItem | HistoryEntry): string {
const paths = entry.files.map((file) => file.path);
const files = paths.slice(0, 2).join(", ") + (paths.length > 2 ? ` +${paths.length - 2}` : "");
const flags =
"undone" in entry ? `${entry.undone ? " [undone]" : ""}${entry.pinned ? " [pinned]" : ""}` : "";
return `${short(entry.id)} ${ago(entry.endedAt)} ${entry.who.name} ${entry.label} (${files})${flags}`;
return `${short(entry.id)} ${ago(entry.endedAt, historyDeps.now())} ${entry.who.name} ${entry.label} (${files})${flags}`;
}

/** The public entry shape: files as paths, no hashes. */
Expand Down
24 changes: 24 additions & 0 deletions packages/cli/src/utils/historyAgo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, expect, it } from "vitest";
import { ago } from "./historyAgo.js";

describe("ago", () => {
const at = 1_000_000_000_000;
const after = (seconds: number) => ago(at, at + seconds * 1000);

it("counts whole seconds, minutes, hours and days", () => {
expect(after(0)).toBe("0s ago");
expect(after(59)).toBe("59s ago");
expect(after(3600)).toBe("1h ago");
expect(after(86_400)).toBe("1d ago");
expect(after(3 * 86_400)).toBe("3d ago");
});

it("never prints a unit at the size of the next one", () => {
expect(after(3570)).toBe("59m ago");
expect(after(86_000)).toBe("23h ago");
});

it("treats a future timestamp as now", () => {
expect(ago(at + 5000, at)).toBe("0s ago");
});
});
8 changes: 8 additions & 0 deletions packages/cli/src/utils/historyAgo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Floor each larger unit: rounding would print "60m ago" at 59.5 minutes and "24h ago" at 23.5 hours.
export function ago(at: number, now: number): string {
const seconds = Math.max(0, Math.round((now - at) / 1000));
if (seconds < 60) return `${seconds}s ago`;
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
if (seconds < 86_400) return `${Math.floor(seconds / 3600)}h ago`;
return `${Math.floor(seconds / 86_400)}d ago`;
}