fix: expose pseudonymized copy location in Raycast

This commit is contained in:
muena
2026-09-27 08:22:06 +02:00
parent 6cb5b0efc5
commit e9cda1c02f
2 changed files with 25 additions and 2 deletions

View File

@@ -218,6 +218,7 @@ function HitReview(props: { root: string; rules: ProjectRule[]; whitelist: Proje
const [stage, setStage] = useState<"selection" | "merge">("selection");
const [personMerges, setPersonMerges] = useState<Record<string, string>>({});
const [busy, setBusy] = useState(false);
const [completed, setCompleted] = useState<{ directory: string; inPlace: boolean; files: number } | null>(null);
const accepted = useMemo(() => new Set(groups.filter((group) => selectedGroups.has(group.key))
.flatMap((group) => group.hits.map((hit) => hit.id))), [groups, selectedGroups]);
const selectedOccurrences = accepted.size;
@@ -351,6 +352,9 @@ function HitReview(props: { root: string; rules: ProjectRule[]; whitelist: Proje
}
toast.style = Toast.Style.Success;
toast.title = inPlace ? `${replacements.length} Dateien ersetzt` : `${run.files.length} Kopien erstellt`;
toast.message = inPlace ? "Die Sicherung ist im Raycast-Datenordner gespeichert."
: "Die Kopien sind im Raycast-Datenordner gespeichert.";
setCompleted({ directory: join(directory, inPlace ? "backup" : "files"), inPlace, files: run.files.length });
await showInFinder(inPlace ? props.root : join(directory, "files")).catch(() => undefined);
} catch (error) {
if (directory && !persisted) await fs.rm(directory, { recursive: true, force: true });
@@ -360,6 +364,18 @@ function HitReview(props: { root: string; rules: ProjectRule[]; whitelist: Proje
} finally { setBusy(false); }
}
if (completed) return <List navigationTitle="Pseudonymisierung abgeschlossen">
<List.Item icon={Icon.CheckCircle}
title={completed.inPlace ? "Projektdateien ersetzt" : `${completed.files} pseudonymisierte Kopien erstellt`}
subtitle={completed.inPlace ? "Die Originale wurden gesichert."
: "Die Originaldateien im Projektordner bleiben unverändert."}
actions={<ActionPanel>
<Action title={completed.inPlace ? "Sicherung im Finder anzeigen" : "Kopien im Finder anzeigen"}
icon={Icon.Folder} onAction={() => showInFinder(completed.directory)} />
<Action.CopyToClipboard title="Speicherpfad kopieren" content={completed.directory} />
</ActionPanel>} />
</List>;
if (stage === "merge") return <List isLoading={busy} navigationTitle="Personen zusammenfassen"
searchBarPlaceholder="Person oder Platzhalter suchen">
<List.Section title="Abschluss">
@@ -415,7 +431,8 @@ function HitReview(props: { root: string; rules: ProjectRule[]; whitelist: Proje
icon={{ source: selectedGroups.has(group.key) ? Icon.CheckCircle : Icon.Circle,
tintColor: selectedGroups.has(group.key) ? Color.Green : Color.SecondaryText }}
title={group.original} subtitle={`→ ${group.placeholder}`}
accessories={[{ text: `${group.hits.length} Vorkommen` }]}
accessories={[{ text: `${group.hits.length} Vorkommen` },
...(group.hits.some((hit) => hit.source === "Kommentarautor") ? [{ text: "Word-Kommentator" }] : [])]}
keywords={[type, labelForType(type)]}
actions={<ActionPanel>
<Action title={selectedGroups.has(group.key) ? "Zuordnung abwählen" : "Zuordnung auswählen"}
@@ -566,7 +583,7 @@ export default function Command() {
onChange={setRules} placeholder={"BEGRIFF: KundenSuite\nPERSON: Max Mustermann"}
info="Ein Begriff pro Zeile. Ohne Typ wird BEGRIFF verwendet. Diese Regeln werden nur für dieses Projekt gespeichert und bei der Velum-Anfrage mitgesendet." />
<Form.TagPicker id="entityTypes" title="Kategorien pseudonymisieren" value={selectedTypes}
onChange={setSelectedTypes} info="Die Auswahl wird beim Prüfen für diesen Projektordner gespeichert. Bei neuen Projekten sind Orte wegen häufiger Fehlalarme zunächst abgewählt.">
onChange={setSelectedTypes} info="Die Auswahl wird beim Prüfen für diesen Projektordner gespeichert. Word-Kommentatoren werden nur mit der Kategorie Personen maskiert. Bei neuen Projekten sind Orte zunächst abgewählt.">
{availableTypes.map((type) => <Form.TagPicker.Item key={type} value={type} title={labelForType(type)} />)}
</Form.TagPicker>
<Form.Description title="Projekt-Whitelist" text={`${whitelist.length} Begriffe werden bei diesem Ordner nicht mehr vorgeschlagen. Über die Aktionen kannst du sie wieder freigeben.`} />

View File

@@ -198,6 +198,9 @@ function ProjectHistory(props: { projectName: string; entries: SavedRun[] }) {
title={new Date(run.createdAt).toLocaleString("de-AT")}
subtitle={`${run.files.length} Dateien`}
actions={<ActionPanel>
<Action title="Pseudonymisierte Kopien im Finder anzeigen" icon={Icon.Folder}
onAction={() => showInFinder(join(directory, "files"))} />
<Action.CopyToClipboard title="Speicherpfad kopieren" content={join(directory, "files")} />
<Action title="Dateien als Klartext exportieren" icon={Icon.Download}
onAction={() => push(<FilePicker run={run} directory={directory} />)} />
</ActionPanel>} />)}
@@ -229,6 +232,9 @@ export default function Command() {
accessories={[{ text: `${run.files.length} Dateien` }, { text: new Date(run.createdAt).toLocaleString("de-AT") },
...(entries.length > 1 ? [{ text: `${entries.length} Stände` }] : [])]}
actions={<ActionPanel>
<Action title="Pseudonymisierte Kopien im Finder anzeigen" icon={Icon.Folder}
onAction={() => showInFinder(join(directory, "files"))} />
<Action.CopyToClipboard title="Speicherpfad kopieren" content={join(directory, "files")} />
<Action title="Aktuellen Stand nachbearbeiten" icon={Icon.Pencil}
onAction={() => push(<RevisionReview saved={{ run, directory }} onSaved={refresh} />)} />
<Action title="Aktuelle Dateien als Klartext exportieren" icon={Icon.Download}