feat: edit in the Changes panel, git graph history, plan quota (v2.3.0)
- Changes panel: a working file opens in a CodeMirror editor with its diff inline (DiffEditor.tsx, @codemirror/merge, lazy-loaded); Changes folds the unchanged lines, Whole file shows them; side by side at 50/50 and full width; a Revert per block; original from HEAD or the branch's merge base (`mergeBase` on GET /git/changes?range=branch) - Every diff: green = line added whole, red = removed whole, blue = edited in place (--diff-mod) - History drawn as a commit graph (#58: `parents` on GET /git/log, --date-order; lib/commitGraph.ts) - Per-workspace never-stage list, drawn orange in the Files tree (--kept); closed Terminal rows past three deleted - Status bar plan quota for Claude (per account) and Codex (GET /api/pricing/quota, server/lib/quota.js); compact hover panel that stays open, Refresh button with a spinner - Settings → Appearance holds language, theme and pet; Ctrl+Shift+C copies; compact sidebar navigation and short session times
This commit is contained in:
@@ -337,23 +337,26 @@ export function formatMs(ms: number): string {
|
||||
* Formats how long ago an ISO/SQLite timestamp was, as a translated relative
|
||||
* string ("just now", "5m ago", "3h ago", "2d ago") using {@link i18n}.
|
||||
* @param iso A past timestamp (see {@link parseDate}).
|
||||
* @param short Drop the "ago" ("now", "5m") — the `common:time.*Short` keys.
|
||||
* @returns A localized relative-time phrase; i18next handles pluralization via `count`.
|
||||
* @remarks Thresholds cascade seconds -> minutes -> hours -> days (days is the largest
|
||||
* bucket, so a 40-day-old event reads "40d ago"). Under a minute collapses to the
|
||||
* "just now" key. Uses translated keys, not `Intl.RelativeTimeFormat`, so the exact
|
||||
* wording is controlled by the `common:time.*` translation resources.
|
||||
*/
|
||||
export function timeAgo(iso: string): string {
|
||||
export function timeAgo(iso: string, short = false): string {
|
||||
const ms = Date.now() - parseDate(iso).getTime();
|
||||
if (Number.isNaN(ms)) return "-";
|
||||
// `short` drops the "ago" ("17m"), for a narrow column like the sidebar's.
|
||||
const k = short ? "Short" : "";
|
||||
const seconds = Math.floor(ms / 1000);
|
||||
if (seconds < 60) return i18n.t("common:time.justNow");
|
||||
if (seconds < 60) return i18n.t(`common:time.justNow${k}`);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 60) return i18n.t("common:time.mAgo", { count: minutes });
|
||||
if (minutes < 60) return i18n.t(`common:time.mAgo${k}`, { count: minutes });
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours < 24) return i18n.t("common:time.hAgo", { count: hours });
|
||||
if (hours < 24) return i18n.t(`common:time.hAgo${k}`, { count: hours });
|
||||
const days = Math.floor(hours / 24);
|
||||
return i18n.t("common:time.dAgo", { count: days });
|
||||
return i18n.t(`common:time.dAgo${k}`, { count: days });
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
Reference in New Issue
Block a user