Improve catalog studio UX and aggressive docker cleanup
Catalog Studio: - Cross-parent drag & drop now uses optimistic updates with rollback on failure (no more full tree reload / visible delay) - Subpage creation adds the node optimistically then refreshes parent only (was full tree reload) - Single page deletion refreshes only the affected parent (was full tree reload) - Root page creation replaces native prompt() with an inline input in the root tab bar - Escape key no longer closes the dialog when an input field is focused - TreeNodeUpdate type now supports parentId and orderNum for optimistic structural changes Docker: - docker-prune.sh default mode now aggressively cleans all unreferenced build cache, images >1h old, and stopped containers >1h old (was 72h/7d/24h which let cache grow past 80% on every push)
This commit is contained in:
1 parent
349188af10
commit
1a9e1e791a
6 files changed
+217
-40
No files matched your search
+10
-12
@@ -2,17 +2,15 @@
|
|||||||
# Reclaim Docker's unused cache so host storage stays bounded.
|
# Reclaim Docker's unused cache so host storage stays bounded.
|
||||||
#
|
#
|
||||||
# Modes:
|
# Modes:
|
||||||
# (default) — gentle, age-windowed (keeps rollback + rebuild speed):
|
# (default) — post-deploy cleanup (safe, fast):
|
||||||
# - BuildKit cache older than 72h, hard-capped at 4 GB (Debian /pnpm store
|
# - ALL unreferenced build cache (new build will re-populate what it needs).
|
||||||
# is shared across builds; everything newer speeds up rebuilds).
|
# - ALL unreferenced images older than 1h (keeps current image + very recent).
|
||||||
# - Images referenced by NO container and older than 7 days.
|
# - ALL stopped containers older than 1h.
|
||||||
# - Containers stopped for more than 24h.
|
# --force — emergency mode ("never let the disk max out"): drops everything
|
||||||
# --force — emergency mode ("never let the disk max out"): drops every age
|
# with no age windows:
|
||||||
# window and reclaims all unused bytes Docker can free:
|
|
||||||
# - ALL unreferenced build cache,
|
# - ALL unreferenced build cache,
|
||||||
# - ALL unreferenced images (no 7-day grace),
|
# - ALL unreferenced images (no age grace),
|
||||||
# - ALL stopped containers.
|
# - ALL stopped containers.
|
||||||
# Trade-off: waiting rebuilds re-fetch deps/images later.
|
|
||||||
#
|
#
|
||||||
# Volumes are NEVER pruned in either mode: mariadb-turbo-data is a database.
|
# Volumes are NEVER pruned in either mode: mariadb-turbo-data is a database.
|
||||||
# Idempotent; exits 0 when Docker is unavailable.
|
# Idempotent; exits 0 when Docker is unavailable.
|
||||||
@@ -41,9 +39,9 @@ if (( FORCE )); then
|
|||||||
docker image prune -af >>"$LOG_FILE" 2>&1 || true
|
docker image prune -af >>"$LOG_FILE" 2>&1 || true
|
||||||
docker container prune -f >>"$LOG_FILE" 2>&1 || true
|
docker container prune -f >>"$LOG_FILE" 2>&1 || true
|
||||||
else
|
else
|
||||||
docker builder prune -af --filter "until=72h" --max-used-space=4g >>"$LOG_FILE" 2>&1 || true
|
docker builder prune -af >>"$LOG_FILE" 2>&1 || true
|
||||||
docker image prune -af --filter "until=168h" >>"$LOG_FILE" 2>&1 || true
|
docker image prune -af --filter "until=1h" >>"$LOG_FILE" 2>&1 || true
|
||||||
docker container prune -f --filter "until=24h" >>"$LOG_FILE" 2>&1 || true
|
docker container prune -f --filter "until=1h" >>"$LOG_FILE" 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '\n[%s] === docker prune complete%s ===\n' "$(now)" "$( (( FORCE )) && printf ' (FORCE)' )" >>"$LOG_FILE"
|
printf '\n[%s] === docker prune complete%s ===\n' "$(now)" "$( (( FORCE )) && printf ' (FORCE)' )" >>"$LOG_FILE"
|
||||||
|
|||||||
@@ -200,6 +200,9 @@ function CatalogManagerInner({
|
|||||||
const onKey = (event: KeyboardEvent) => {
|
const onKey = (event: KeyboardEvent) => {
|
||||||
if (embedded || event.key !== "Escape" || event.defaultPrevented) return;
|
if (embedded || event.key !== "Escape" || event.defaultPrevented) return;
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
|
// Never steal Escape from inputs/comboboxes (e.g. root name field).
|
||||||
|
const tag = target?.tagName;
|
||||||
|
if (tag === "INPUT" || tag === "TEXTAREA") return;
|
||||||
const focusedDialog = target.closest?.("[role=dialog]");
|
const focusedDialog = target.closest?.("[role=dialog]");
|
||||||
if (
|
if (
|
||||||
focusedDialog &&
|
focusedDialog &&
|
||||||
@@ -282,26 +285,26 @@ function CatalogManagerInner({
|
|||||||
dispatch({ type: "SET_ACTIVE_TAB", id });
|
dispatch({ type: "SET_ACTIVE_TAB", id });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateRoot = async () => {
|
const handleCreateRoot = async (caption: string): Promise<boolean> => {
|
||||||
if (!canEdit) return;
|
if (!canEdit) return false;
|
||||||
const caption = prompt("Root page name:");
|
|
||||||
if (!caption?.trim()) return;
|
|
||||||
try {
|
try {
|
||||||
const result =
|
const result =
|
||||||
catalogType === "bc"
|
catalogType === "bc"
|
||||||
? await createBcPage({
|
? await createBcPage({
|
||||||
parentId: -1,
|
parentId: -1,
|
||||||
caption: caption.trim(),
|
caption,
|
||||||
})
|
})
|
||||||
: await createCatalogPage({
|
: await createCatalogPage({
|
||||||
parentId: -1,
|
parentId: -1,
|
||||||
caption: caption.trim(),
|
caption,
|
||||||
});
|
});
|
||||||
if (!result.ok) throw new Error(result.error);
|
if (!result.ok) throw new Error(result.error);
|
||||||
await loadRootPages();
|
await loadRootPages();
|
||||||
toast.success("Root page created");
|
toast.success("Root page created");
|
||||||
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
toast.error("Failed to create root page");
|
toast.error("Failed to create root page");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -233,6 +233,12 @@ function reducer(
|
|||||||
...(action.update.iconImage !== undefined && {
|
...(action.update.iconImage !== undefined && {
|
||||||
iconImage: action.update.iconImage,
|
iconImage: action.update.iconImage,
|
||||||
}),
|
}),
|
||||||
|
...(action.update.parentId !== undefined && {
|
||||||
|
parentId: action.update.parentId,
|
||||||
|
}),
|
||||||
|
...(action.update.orderNum !== undefined && {
|
||||||
|
orderNum: action.update.orderNum,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return { ...state, nodes: nextNodes };
|
return { ...state, nodes: nextNodes };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ export interface TreeNodeUpdate {
|
|||||||
visible?: string;
|
visible?: string;
|
||||||
pageLayout?: string;
|
pageLayout?: string;
|
||||||
iconImage?: number;
|
iconImage?: number;
|
||||||
|
parentId?: number;
|
||||||
|
orderNum?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RootTab {
|
export interface RootTab {
|
||||||
|
|||||||
Reference in new issue
Block a user