225 lines
7.0 KiB
TypeScript
225 lines
7.0 KiB
TypeScript
import {
|
|
Action,
|
|
ActionPanel,
|
|
Detail,
|
|
Icon,
|
|
List,
|
|
showToast,
|
|
Toast,
|
|
} from "@raycast/api";
|
|
import { useEffect, useState } from "react";
|
|
import { canonicalProjectPath, listAllRuns } from "./lib/velum-project";
|
|
import { listSessions } from "./sessions";
|
|
import type { PlaceholderMapping } from "./types";
|
|
import { markdownCodeBlock } from "./ui";
|
|
|
|
type TableSource = {
|
|
id: string;
|
|
title: string;
|
|
subtitle: string;
|
|
mapping: PlaceholderMapping;
|
|
};
|
|
|
|
export function TranslationTable({
|
|
title,
|
|
mapping,
|
|
}: {
|
|
title: string;
|
|
mapping: PlaceholderMapping;
|
|
}) {
|
|
const groups = new Map<string, Array<[string, PlaceholderMapping[string]]>>();
|
|
for (const entry of Object.entries(mapping).sort(([a], [b]) =>
|
|
a.localeCompare(b),
|
|
)) {
|
|
const type = entry[1].type || "Sonstige";
|
|
const entries = groups.get(type) ?? [];
|
|
entries.push(entry);
|
|
groups.set(type, entries);
|
|
}
|
|
|
|
return (
|
|
<List
|
|
navigationTitle={`Übersetzungstabelle · ${title}`}
|
|
searchBarPlaceholder="Platzhalter oder Klartext suchen"
|
|
>
|
|
<List.EmptyView
|
|
title="Keine Zuordnungen"
|
|
description="Für diesen Stand sind noch keine Übersetzungen gespeichert."
|
|
/>
|
|
{[...groups]
|
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
.map(([type, entries]) => (
|
|
<List.Section key={type} title={`${type} · ${entries.length}`}>
|
|
{entries.map(([placeholder, entry]) => (
|
|
<List.Item
|
|
key={placeholder}
|
|
icon={Icon.ArrowRight}
|
|
title={placeholder}
|
|
subtitle={entry.original}
|
|
keywords={[entry.original, entry.type, entry.merged_into ?? ""]}
|
|
accessories={
|
|
entry.merged_into ? [{ text: `→ ${entry.merged_into}` }] : []
|
|
}
|
|
actions={
|
|
<ActionPanel>
|
|
<Action.Push
|
|
title="Eintrag vollständig anzeigen"
|
|
icon={Icon.Eye}
|
|
target={
|
|
<Detail
|
|
navigationTitle={placeholder}
|
|
markdown={[
|
|
`# ${entry.type}`,
|
|
"",
|
|
"Platzhalter",
|
|
markdownCodeBlock(placeholder),
|
|
"",
|
|
"Klartext",
|
|
markdownCodeBlock(entry.original),
|
|
...(entry.merged_into
|
|
? [
|
|
"",
|
|
"Zusammengeführt mit",
|
|
markdownCodeBlock(entry.merged_into),
|
|
]
|
|
: []),
|
|
].join("\n")}
|
|
actions={
|
|
<ActionPanel>
|
|
<Action.CopyToClipboard
|
|
title="Platzhalter kopieren"
|
|
content={placeholder}
|
|
/>
|
|
<Action.CopyToClipboard
|
|
title="Klartext kopieren"
|
|
content={entry.original}
|
|
/>
|
|
</ActionPanel>
|
|
}
|
|
/>
|
|
}
|
|
/>
|
|
<Action.CopyToClipboard
|
|
title="Platzhalter kopieren"
|
|
content={placeholder}
|
|
/>
|
|
<Action.CopyToClipboard
|
|
title="Klartext kopieren"
|
|
content={entry.original}
|
|
/>
|
|
</ActionPanel>
|
|
}
|
|
/>
|
|
))}
|
|
</List.Section>
|
|
))}
|
|
</List>
|
|
);
|
|
}
|
|
|
|
export default function Command() {
|
|
const [projects, setProjects] = useState<TableSource[]>([]);
|
|
const [sessions, setSessions] = useState<TableSource[]>([]);
|
|
const [busy, setBusy] = useState(true);
|
|
|
|
useEffect(() => {
|
|
Promise.all([listAllRuns(), listSessions()])
|
|
.then(([runs, savedSessions]) => {
|
|
const latest = new Map<string, TableSource>();
|
|
for (const { run } of runs) {
|
|
const path = canonicalProjectPath(run.projectPath);
|
|
if (!latest.has(path))
|
|
latest.set(path, {
|
|
id: path,
|
|
title: run.projectName,
|
|
subtitle: `${path} · ${new Date(run.createdAt).toLocaleString("de-AT")}`,
|
|
mapping: run.mapping,
|
|
});
|
|
}
|
|
setProjects(
|
|
[...latest.values()].sort((a, b) => a.title.localeCompare(b.title)),
|
|
);
|
|
setSessions(
|
|
savedSessions.map((session) => ({
|
|
id: session.id,
|
|
title: session.name,
|
|
subtitle: `${Object.keys(session.mapping).length} Zuordnungen · ${new Date(session.updatedAt).toLocaleString("de-AT")}`,
|
|
mapping: session.mapping,
|
|
})),
|
|
);
|
|
})
|
|
.catch((error: Error) =>
|
|
showToast({
|
|
style: Toast.Style.Failure,
|
|
title: "Übersetzungstabellen nicht lesbar",
|
|
message: error.message,
|
|
}),
|
|
)
|
|
.finally(() => setBusy(false));
|
|
}, []);
|
|
|
|
return (
|
|
<List
|
|
isLoading={busy}
|
|
navigationTitle="Übersetzungstabellen"
|
|
searchBarPlaceholder="Projekt oder Sitzung suchen"
|
|
>
|
|
<List.EmptyView
|
|
title="Keine Tabellen"
|
|
description="Pseudonymisiere zuerst Text oder Projektdateien."
|
|
/>
|
|
<List.Section title="Projekte">
|
|
{projects.map((source) => (
|
|
<List.Item
|
|
key={source.id}
|
|
icon={Icon.Folder}
|
|
title={source.title}
|
|
subtitle={source.subtitle}
|
|
accessories={[
|
|
{ text: `${Object.keys(source.mapping).length} Zuordnungen` },
|
|
]}
|
|
actions={
|
|
<ActionPanel>
|
|
<Action.Push
|
|
title="Übersetzungstabelle anzeigen"
|
|
icon={Icon.List}
|
|
target={
|
|
<TranslationTable
|
|
title={source.title}
|
|
mapping={source.mapping}
|
|
/>
|
|
}
|
|
/>
|
|
</ActionPanel>
|
|
}
|
|
/>
|
|
))}
|
|
</List.Section>
|
|
<List.Section title="Sitzungen">
|
|
{sessions.map((source) => (
|
|
<List.Item
|
|
key={source.id}
|
|
icon={Icon.Person}
|
|
title={source.title}
|
|
subtitle={source.subtitle}
|
|
actions={
|
|
<ActionPanel>
|
|
<Action.Push
|
|
title="Übersetzungstabelle anzeigen"
|
|
icon={Icon.List}
|
|
target={
|
|
<TranslationTable
|
|
title={source.title}
|
|
mapping={source.mapping}
|
|
/>
|
|
}
|
|
/>
|
|
</ActionPanel>
|
|
}
|
|
/>
|
|
))}
|
|
</List.Section>
|
|
</List>
|
|
);
|
|
}
|