feat: show project and session translation tables
This commit is contained in:
@@ -89,6 +89,12 @@
|
|||||||
"description": "Zwischenablage übernehmen, Projekt oder Sitzung wählen und wiederherstellen.",
|
"description": "Zwischenablage übernehmen, Projekt oder Sitzung wählen und wiederherstellen.",
|
||||||
"mode": "view"
|
"mode": "view"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "show-translation-tables",
|
||||||
|
"title": "Übersetzungstabellen anzeigen",
|
||||||
|
"description": "Gespeicherte Zuordnungen je Projekt oder Sitzung durchsuchen.",
|
||||||
|
"mode": "view"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "manage-sessions",
|
"name": "manage-sessions",
|
||||||
"title": "Sitzungen verwalten",
|
"title": "Sitzungen verwalten",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
} from "./sessions";
|
} from "./sessions";
|
||||||
import type { VelumSession } from "./types";
|
import type { VelumSession } from "./types";
|
||||||
import { mappingSummary, sessionSubtitle } from "./ui";
|
import { mappingSummary, sessionSubtitle } from "./ui";
|
||||||
|
import { TranslationTable } from "./show-translation-tables";
|
||||||
|
|
||||||
export default function Command() {
|
export default function Command() {
|
||||||
const [sessions, setSessions] = useState<VelumSession[]>([]);
|
const [sessions, setSessions] = useState<VelumSession[]>([]);
|
||||||
@@ -145,6 +146,11 @@ export default function Command() {
|
|||||||
accessories={[{ text: `${countMappingEntries(session.mapping)}` }]}
|
accessories={[{ text: `${countMappingEntries(session.mapping)}` }]}
|
||||||
actions={
|
actions={
|
||||||
<ActionPanel>
|
<ActionPanel>
|
||||||
|
<Action.Push
|
||||||
|
title="Übersetzungstabelle anzeigen"
|
||||||
|
icon={Icon.List}
|
||||||
|
target={<TranslationTable title={session.name} mapping={session.mapping} />}
|
||||||
|
/>
|
||||||
<Action
|
<Action
|
||||||
title="Als Aktive Sitzung Setzen"
|
title="Als Aktive Sitzung Setzen"
|
||||||
icon={Icon.CheckCircle}
|
icon={Icon.CheckCircle}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { canonicalProjectPath, listAllRuns, resolveWithin, restorePath, restoreT
|
|||||||
import type { ProjectFile, ProjectRun } from "./lib/velum-project";
|
import type { ProjectFile, ProjectRun } from "./lib/velum-project";
|
||||||
import { canonicalPerson, mergePeople } from "./lib/project-review";
|
import { canonicalPerson, mergePeople } from "./lib/project-review";
|
||||||
import { planProjectRevision, reviseProjectRun } from "./lib/project-revision";
|
import { planProjectRevision, reviseProjectRun } from "./lib/project-revision";
|
||||||
|
import { TranslationTable } from "./show-translation-tables";
|
||||||
|
|
||||||
type SavedRun = { run: ProjectRun; directory: string };
|
type SavedRun = { run: ProjectRun; directory: string };
|
||||||
|
|
||||||
@@ -229,6 +230,9 @@ function ProjectHistory(props: { projectName: string; entries: SavedRun[] }) {
|
|||||||
title={new Date(run.createdAt).toLocaleString("de-AT")}
|
title={new Date(run.createdAt).toLocaleString("de-AT")}
|
||||||
subtitle={`${run.files.length} Dateien`}
|
subtitle={`${run.files.length} Dateien`}
|
||||||
actions={<ActionPanel>
|
actions={<ActionPanel>
|
||||||
|
<Action.Push title="Übersetzungstabelle anzeigen" icon={Icon.List}
|
||||||
|
target={<TranslationTable title={`${props.projectName} · ${new Date(run.createdAt).toLocaleString("de-AT")}`}
|
||||||
|
mapping={run.mapping} />} />
|
||||||
<Action title="Pseudonymisierte Kopien im Finder anzeigen" icon={Icon.Folder}
|
<Action title="Pseudonymisierte Kopien im Finder anzeigen" icon={Icon.Folder}
|
||||||
onAction={() => showInFinder(runCopiesPath(run, directory))} />
|
onAction={() => showInFinder(runCopiesPath(run, directory))} />
|
||||||
<Action.CopyToClipboard title="Speicherpfad kopieren" content={runCopiesPath(run, directory)} />
|
<Action.CopyToClipboard title="Speicherpfad kopieren" content={runCopiesPath(run, directory)} />
|
||||||
@@ -265,6 +269,8 @@ export default function Command() {
|
|||||||
accessories={[{ text: `${run.files.length} Dateien` }, { text: new Date(run.createdAt).toLocaleString("de-AT") },
|
accessories={[{ text: `${run.files.length} Dateien` }, { text: new Date(run.createdAt).toLocaleString("de-AT") },
|
||||||
...(entries.length > 1 ? [{ text: `${entries.length} Stände` }] : [])]}
|
...(entries.length > 1 ? [{ text: `${entries.length} Stände` }] : [])]}
|
||||||
actions={<ActionPanel>
|
actions={<ActionPanel>
|
||||||
|
<Action.Push title="Übersetzungstabelle anzeigen" icon={Icon.List}
|
||||||
|
target={<TranslationTable title={run.projectName} mapping={run.mapping} />} />
|
||||||
<Action title="Projektdateien als Klartext wiederherstellen" icon={Icon.Download}
|
<Action title="Projektdateien als Klartext wiederherstellen" icon={Icon.Download}
|
||||||
onAction={() => push(<FilePicker run={run} directory={directory} />)} />
|
onAction={() => push(<FilePicker run={run} directory={directory} />)} />
|
||||||
<Action title="Pseudonymisierte Kopien im Finder anzeigen" icon={Icon.Folder}
|
<Action title="Pseudonymisierte Kopien im Finder anzeigen" icon={Icon.Folder}
|
||||||
|
|||||||
224
src/show-translation-tables.tsx
Normal file
224
src/show-translation-tables.tsx
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user