Organize imports: persist undo across sessions, translate UI to 23 languages
CI / check (push) Failing after 5m7s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
CI / publish-container (push) Skipped

This commit is contained in:
openhands committed 2026-09-14 19:07:56 +02:00
1 parent 3a4089a5fa
commit 0f01ebf48b
24 files changed
+1717 -1662

No files matched your search

@@ -60,6 +60,7 @@ const QUICK_PICKS = 40;
const DAYS_OPTIONS = [30, 60, 90, 180] as const;
const LS_KEY = "epicnexst:organizePrefs";
const HIST_KEY = "epicnexst:organizeHistory";
interface OrganizePrefs {
moveMode?: boolean;
@@ -126,6 +127,41 @@ type MoveEntry =
to: string;
};
/** Restore a previously persisted undo history, dropping corrupted entries. */
function readHistory(): MoveEntry[] {
if (typeof window === "undefined") return [];
try {
const raw = window.localStorage.getItem(HIST_KEY);
if (!raw) return [];
const parsed: unknown = JSON.parse(raw);
if (!Array.isArray(parsed)) return [];
return parsed.filter((entry): entry is MoveEntry => {
if (!entry || typeof entry !== "object") return false;
const e = entry as Partial<MoveEntry>;
if (e.kind === "item") {
return (
typeof e.from === "string" &&
typeof e.to === "string" &&
e.item != null &&
typeof e.item.itemId === "number"
);
}
if (e.kind === "batch") {
return (
typeof e.to === "string" &&
e.from != null &&
typeof e.from === "object" &&
Array.isArray(e.items)
);
}
return false;
});
} catch {
// ignore corrupt storage
}
return [];
}
interface OrganizeImportsDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
@@ -252,7 +288,7 @@ export function OrganizeImportsDialog({
setApproved({});
setOverrides({});
setSelection(new Set());
setHistory([]);
setHistory(readHistory());
setDrag(null);
setHoverGroup(null);
setParentId(prefs.parentId ?? -1);
@@ -310,6 +346,16 @@ export function OrganizeImportsDialog({
destinationCaption,
]);
// Persist the undo history so moves survive closing and reopening the dialog.
useEffect(() => {
if (typeof window === "undefined") return;
try {
window.localStorage.setItem(HIST_KEY, JSON.stringify(history));
} catch {
// ignore quota / privacy-mode write errors
}
}, [history]);
useEffect(() => {
const t = setTimeout(() => setDestDebounced(destQuery.trim()), 300);
return () => clearTimeout(t);
@@ -680,6 +726,15 @@ export function OrganizeImportsDialog({
}
const moved = created.reduce((sum, c) => sum + c.moved, 0);
const added = created.reduce((sum, c) => sum + c.added, 0);
if (created.length > 0) {
// The server state changed, so client-side undo is no longer valid.
setHistory([]);
try {
window.localStorage.removeItem(HIST_KEY);
} catch {
// ignore storage errors
}
}
if (failedRuns > 0) {
toast.error(
t("errorPartial", { failed: failedRuns, total: runs.length }),