Make Applications a real app manager, and clean up storage without the racket
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -40,6 +40,32 @@ Singleton {
|
||||
// are different answers.
|
||||
property bool foldersMeasured: false
|
||||
|
||||
// What the used space is made of, and what could be freed.
|
||||
//
|
||||
// { segments: { home, applications, caches, system, free }, usedBytes,
|
||||
// totalBytes, freeBytes, complete, exceedsUsed }
|
||||
//
|
||||
// `system` is the remainder -- used bytes minus the three measured segments
|
||||
// -- and the page calls it "System & everything else" for that reason. It
|
||||
// is not a measurement of the system; it is everything the walk did not
|
||||
// reach. The segments never sum past used, and when a measurement would
|
||||
// overshoot, `exceedsUsed` says so instead of a number being scaled to make
|
||||
// the bar look tidy.
|
||||
property var breakdown: null
|
||||
property bool breakdownMeasured: false
|
||||
property bool measuringBreakdown: false
|
||||
|
||||
// [{ id, label, detail, bytes, privileged }]
|
||||
//
|
||||
// Nothing here is selected, ordered by urgency, or acted on. Each row is
|
||||
// freed only by its own id being passed to clean(), which is what keeps a
|
||||
// mis-wired button from emptying four things at once.
|
||||
property var cleanables: []
|
||||
property bool cleanablesMeasured: false
|
||||
property bool measuringCleanables: false
|
||||
// Which row is mid-clean, so the page can disable that one row.
|
||||
property string cleaningId: ""
|
||||
|
||||
readonly property var primaryDrive: root.drives.length > 0 ? root.drives[0] : null
|
||||
|
||||
// The filesystem the user means when they ask how full the machine is.
|
||||
@@ -110,6 +136,49 @@ Singleton {
|
||||
folderScan.running = true;
|
||||
}
|
||||
|
||||
// The same walk `scan` does, so it costs the same and is asked for on
|
||||
// demand rather than when the page opens.
|
||||
function measureBreakdown(): void {
|
||||
if (root.measuringBreakdown)
|
||||
return;
|
||||
root.measuringBreakdown = true;
|
||||
breakdownScan.running = true;
|
||||
}
|
||||
|
||||
function measureCleanables(): void {
|
||||
if (root.measuringCleanables)
|
||||
return;
|
||||
root.measuringCleanables = true;
|
||||
cleanableScan.running = true;
|
||||
}
|
||||
|
||||
// Exactly one, named. An id this service has not been told about is
|
||||
// refused here and refused again by the helper.
|
||||
function clean(identifier: string): void {
|
||||
if (cleaner.running || root.measuringCleanables)
|
||||
return;
|
||||
const known = root.cleanables.some(item => String(item.id) === String(identifier));
|
||||
if (!known) {
|
||||
root.lastError = "There is nothing by that name to clean up.";
|
||||
return;
|
||||
}
|
||||
root.lastError = "";
|
||||
root.cleaningId = String(identifier);
|
||||
cleaner.command = [root.helperPath, "clean", String(identifier)];
|
||||
cleaner.running = true;
|
||||
}
|
||||
|
||||
function absorbCleanables(text: string): void {
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
root.cleanables = Array.isArray(parsed) ? parsed : [];
|
||||
root.cleanablesMeasured = true;
|
||||
} catch (error) {
|
||||
root.lastError = "Could not measure what could be cleaned up.";
|
||||
console.warn("Disks: could not parse cleanables output:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function unmount(devicePath: string): void {
|
||||
root.runMedia(["unmount", devicePath]);
|
||||
}
|
||||
@@ -171,6 +240,54 @@ Singleton {
|
||||
onExited: root.scanning = false
|
||||
}
|
||||
|
||||
Process {
|
||||
id: breakdownScan
|
||||
command: [root.helperPath, "breakdown"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
root.breakdown = JSON.parse(this.text);
|
||||
root.breakdownMeasured = true;
|
||||
} catch (error) {
|
||||
root.lastError = "Could not measure what is using the drive.";
|
||||
console.warn("Disks: could not parse breakdown output:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: if (this.text.trim() !== "") root.lastError = this.text.trim()
|
||||
}
|
||||
onExited: root.measuringBreakdown = false
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cleanableScan
|
||||
command: [root.helperPath, "cleanables"]
|
||||
stdout: StdioCollector { onStreamFinished: root.absorbCleanables(this.text) }
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: if (this.text.trim() !== "") root.lastError = this.text.trim()
|
||||
}
|
||||
onExited: root.measuringCleanables = false
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cleaner
|
||||
// The helper answers with the fresh list, so the sizes on screen are
|
||||
// what is there now rather than what was there before the clean.
|
||||
stdout: StdioCollector { onStreamFinished: root.absorbCleanables(this.text) }
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: if (this.text.trim() !== "") root.lastError = this.text.trim()
|
||||
}
|
||||
onExited: {
|
||||
root.cleaningId = "";
|
||||
// Freeing space changes the drive's usage and its breakdown, and
|
||||
// both are on screen while this happens.
|
||||
root.refresh();
|
||||
if (root.breakdownMeasured)
|
||||
root.measureBreakdown();
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: media
|
||||
stderr: StdioCollector {
|
||||
|
||||
Reference in New Issue
Block a user