papilot 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +4 -2
  2. package/bin/doctor.mjs +1 -1
  3. package/dist/assets/{_virtual_papilot-licenses-CMfCtEYQ.js → _virtual_papilot-licenses-YJbh9c1G.js} +1 -1
  4. package/dist/assets/index-0siHHtfz.js +267 -0
  5. package/dist/assets/index-CxdYm81a.css +1 -0
  6. package/dist/assets/{math-Bu3xEyrC.js → math-ibBpTslk.js} +1 -1
  7. package/dist/assets/{pdf-view-PbkCuTZ-.js → pdf-view-CD1kNKYH.js} +1 -1
  8. package/dist/index.html +2 -2
  9. package/engine/dist/web/assets/FullPaper-DUgLb01O.js +54 -0
  10. package/engine/dist/web/assets/index-D3_ZFdT8.css +1 -0
  11. package/engine/dist/web/assets/index-DJywe0wU.js +19 -0
  12. package/engine/dist/web/assets/pdf.worker.min-CjEcRF4W.mjs +28 -0
  13. package/{vendor/latex-review → engine}/dist/web/index.html +2 -2
  14. package/engine/package-lock.json +3390 -0
  15. package/engine/package.json +50 -0
  16. package/{vendor/latex-review → engine}/src/core/build.ts +198 -99
  17. package/{vendor/latex-review → engine}/src/core/git.ts +133 -28
  18. package/{vendor/latex-review → engine}/src/core/outline.ts +0 -9
  19. package/engine/src/core/progress.ts +67 -0
  20. package/{vendor/latex-review → engine}/src/core/review.ts +32 -0
  21. package/engine/src/locales.ts +47 -0
  22. package/{vendor/latex-review → engine}/src/render/inline.ts +1 -0
  23. package/{vendor/latex-review → engine}/src/render/locate.ts +26 -16
  24. package/engine/src/render/positions.ts +34 -0
  25. package/engine/src/render/synctex.ts +188 -0
  26. package/engine/src/render/synctex.wasm +0 -0
  27. package/engine/src/render/synctex.wasm.LICENSE +50 -0
  28. package/{vendor/latex-review → engine}/src/server.ts +5 -3
  29. package/{vendor/latex-review → engine}/src/types.ts +13 -0
  30. package/engine/src/web/FullPaper.tsx +318 -0
  31. package/engine/src/web/i18n.ts +36 -0
  32. package/engine/src/web/locales/de.json +253 -0
  33. package/engine/src/web/locales/es.json +253 -0
  34. package/engine/src/web/locales/fr.json +253 -0
  35. package/engine/src/web/locales/index.ts +16 -0
  36. package/engine/src/web/locales/it.json +253 -0
  37. package/engine/src/web/locales/ja.json +253 -0
  38. package/engine/src/web/locales/ko.json +253 -0
  39. package/engine/src/web/locales/pt.json +253 -0
  40. package/engine/src/web/locales/sv.json +253 -0
  41. package/engine/src/web/locales/zh-Hant.json +253 -0
  42. package/engine/src/web/paper.ts +13 -0
  43. package/{vendor/latex-review → engine}/src/web/references.tsx +1 -1
  44. package/{vendor/latex-review → engine}/src/web/review.tsx +99 -2
  45. package/{vendor/latex-review → engine}/src/web/style.css +71 -11
  46. package/engine/src/web/view-preferences.ts +13 -0
  47. package/{vendor/latex-review → engine}/src/web/zh-CN.ts +3 -0
  48. package/package.json +18 -9
  49. package/src/conversation.ts +1 -1
  50. package/src/server/acp.ts +9 -5
  51. package/src/server/agents.ts +4 -2
  52. package/src/server/app.ts +23 -0
  53. package/src/server/changes.ts +12 -3
  54. package/src/server/imports.ts +69 -25
  55. package/src/server/main.ts +3 -2
  56. package/src/server/manuscript.ts +31 -12
  57. package/src/server/paths.ts +1 -1
  58. package/src/server/start.ts +3 -3
  59. package/src/server/store.ts +0 -8
  60. package/src/server/update-install.ts +6 -3
  61. package/src/server/updates.ts +55 -23
  62. package/src/server/workspace.ts +24 -2
  63. package/src/shared.ts +7 -1
  64. package/src/skill-metadata.ts +1 -1
  65. package/vendor/latex-review/package-lock.json +4 -3106
  66. package/vendor/latex-review/package.json +3 -46
  67. package/README.zh-CN.md +0 -100
  68. package/dist/assets/index-BlLyIqT9.js +0 -265
  69. package/dist/assets/index-DVZtyZvu.css +0 -1
  70. package/vendor/latex-review/dist/web/assets/index-CXdnUjct.js +0 -19
  71. package/vendor/latex-review/dist/web/assets/index-DEz-LsGl.css +0 -1
  72. package/vendor/latex-review/src/web/i18n.ts +0 -48
  73. /package/{vendor/latex-review → engine}/LICENSE +0 -0
  74. /package/{vendor/latex-review → engine}/dist/web/assets/TeXGyrePagellaX-Bold-CEtYdOXj.otf +0 -0
  75. /package/{vendor/latex-review → engine}/dist/web/assets/TeXGyrePagellaX-Regular-DQJjZE_L.otf +0 -0
  76. /package/{vendor/latex-review → engine}/src/cli.ts +0 -0
  77. /package/{vendor/latex-review → engine}/src/client.ts +0 -0
  78. /package/{vendor/latex-review → engine}/src/core/bib.ts +0 -0
  79. /package/{vendor/latex-review → engine}/src/core/changes.ts +0 -0
  80. /package/{vendor/latex-review → engine}/src/core/config.ts +0 -0
  81. /package/{vendor/latex-review → engine}/src/core/current-commit.ts +0 -0
  82. /package/{vendor/latex-review → engine}/src/core/delivery.ts +0 -0
  83. /package/{vendor/latex-review → engine}/src/core/diff.ts +0 -0
  84. /package/{vendor/latex-review → engine}/src/core/files.ts +0 -0
  85. /package/{vendor/latex-review → engine}/src/core/process.ts +0 -0
  86. /package/{vendor/latex-review → engine}/src/core/publish.ts +0 -0
  87. /package/{vendor/latex-review → engine}/src/core/reconcile.ts +0 -0
  88. /package/{vendor/latex-review → engine}/src/core/store.ts +0 -0
  89. /package/{vendor/latex-review → engine}/src/core/tasks.ts +0 -0
  90. /package/{vendor/latex-review → engine}/src/core/verify.ts +0 -0
  91. /package/{vendor/latex-review → engine}/src/core/view.ts +0 -0
  92. /package/{vendor/latex-review → engine}/src/render/effects.ts +0 -0
  93. /package/{vendor/latex-review → engine}/src/render/pdf.ts +0 -0
  94. /package/{vendor/latex-review → engine}/src/render/pdfdiff.ts +0 -0
  95. /package/{vendor/latex-review → engine}/src/render/round.ts +0 -0
  96. /package/{vendor/latex-review → engine}/src/render/worker.ts +0 -0
  97. /package/{vendor/latex-review → engine}/src/runtime.ts +0 -0
  98. /package/{vendor/latex-review → engine}/src/web/ChangeMap.tsx +0 -0
  99. /package/{vendor/latex-review → engine}/src/web/PdfStrip.tsx +0 -0
  100. /package/{vendor/latex-review → engine}/src/web/effect-preferences.ts +0 -0
  101. /package/{vendor/latex-review → engine}/src/web/fonts/GUST-FONT-LICENSE.txt +0 -0
  102. /package/{vendor/latex-review → engine}/src/web/fonts/LPPL.txt +0 -0
  103. /package/{vendor/latex-review → engine}/src/web/fonts/MANIFEST-newpx.txt +0 -0
  104. /package/{vendor/latex-review → engine}/src/web/fonts/README-newpx.txt +0 -0
  105. /package/{vendor/latex-review → engine}/src/web/fonts/README.md +0 -0
  106. /package/{vendor/latex-review → engine}/src/web/fonts/TeXGyrePagellaX-Bold.otf +0 -0
  107. /package/{vendor/latex-review → engine}/src/web/fonts/TeXGyrePagellaX-Regular.otf +0 -0
  108. /package/{vendor/latex-review → engine}/src/web/index.html +0 -0
  109. /package/{vendor/latex-review → engine}/src/web/main.tsx +0 -0
  110. /package/{vendor/latex-review → engine}/src/web/model.ts +0 -0
  111. /package/{vendor/latex-review → engine}/src/web/overview.tsx +0 -0
  112. /package/{vendor/latex-review → engine}/src/web/shortcuts.ts +0 -0
@@ -0,0 +1,318 @@
1
+ import {
2
+ Fragment,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ type ReactNode,
8
+ type RefObject,
9
+ } from "react";
10
+ import * as pdfjs from "pdfjs-dist";
11
+ import type { PDFDocumentProxy, RenderTask } from "pdfjs-dist";
12
+ import worker from "pdfjs-dist/build/pdf.worker.min.mjs?url";
13
+ import type { ChangeBlock, Strip } from "../types.ts";
14
+ import { t } from "./i18n.ts";
15
+ import type { PlacedChange } from "./paper.ts";
16
+
17
+ // Built assets share one folder, and a host such as Papilot serves the viewer
18
+ // under its own prefix: find the worker next to this chunk, not at /assets.
19
+ pdfjs.GlobalWorkerOptions.workerSrc = import.meta.env.DEV
20
+ ? worker
21
+ : new URL(worker.split("/").pop()!, import.meta.url).href;
22
+
23
+ // The whole paper with its changes in place: the new PDF's pages, opened up
24
+ // where a change's new lines are, and that change shown there as in the
25
+ // changes view, old lines above new. Pages are painted only near the
26
+ // viewport, so a long thesis scrolls like a short paper.
27
+ interface Cut {
28
+ top: number;
29
+ bottom: number;
30
+ // Changes shown at this cut: the one whose new lines start here, and
31
+ // changes without new lines of their own that follow it.
32
+ shown: PlacedChange[];
33
+ }
34
+ interface Paper {
35
+ pdf: PDFDocumentProxy;
36
+ width: number;
37
+ height: number;
38
+ pages: Cut[][];
39
+ before: PlacedChange[];
40
+ }
41
+
42
+ // The page of this PDF that shows a strip of the round's PDF. A round may
43
+ // compile only the changed chapters, so pages are matched by their labels,
44
+ // moving forward through the paper.
45
+ function locator(count: number, labels: string[] | null) {
46
+ let cursor = 0;
47
+ return (strip: Strip) => {
48
+ if (!labels) return strip.page - 1 < count ? strip.page - 1 : -1;
49
+ for (const from of [cursor, 0]) {
50
+ const found = labels.indexOf(strip.label, from);
51
+ if (found >= 0) return (cursor = found);
52
+ }
53
+ return -1;
54
+ };
55
+ }
56
+
57
+ function plan(count: number, labels: string[] | null, changes: PlacedChange[]) {
58
+ const pages: Cut[][] = Array.from({ length: count }, () => []),
59
+ before: PlacedChange[] = [],
60
+ find = locator(count, labels);
61
+ let last: Cut | null = null;
62
+ for (const change of changes) {
63
+ const spots = change.block.after.map((strip) => ({ strip, page: strip.top === undefined ? -1 : find(strip) }));
64
+ if (!spots.length || spots.some((s) => s.page < 0)) {
65
+ // No new lines here (a deletion), or none this PDF has: after the
66
+ // previous change, or before the paper when it is the first.
67
+ if (last) last.shown.push(change);
68
+ else before.push(change);
69
+ continue;
70
+ }
71
+ for (const [k, { strip, page }] of spots.entries()) {
72
+ const cut: Cut = { top: strip.top!, bottom: strip.top! + strip.height, shown: k ? [] : [change] };
73
+ pages[page].push(cut);
74
+ if (!k) last = cut;
75
+ }
76
+ }
77
+ for (const cuts of pages) {
78
+ cuts.sort((a, b) => a.top - b.top);
79
+ for (let i = 1; i < cuts.length; i++) {
80
+ cuts[i].top = Math.max(cuts[i].top, cuts[i - 1].bottom);
81
+ cuts[i].bottom = Math.max(cuts[i].bottom, cuts[i].top);
82
+ }
83
+ }
84
+ return { pages, before };
85
+ }
86
+
87
+ const clock = (ms: number) => {
88
+ const seconds = Math.max(0, Math.floor(ms / 1000));
89
+ return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`;
90
+ };
91
+
92
+ export function FullPaper({
93
+ url,
94
+ changes,
95
+ render,
96
+ scrollRoot,
97
+ onReady,
98
+ onUnavailable,
99
+ }: {
100
+ url: string;
101
+ changes: PlacedChange[];
102
+ render: (block: ChangeBlock, index: number) => ReactNode;
103
+ scrollRoot: RefObject<HTMLElement | null>;
104
+ onReady: () => void;
105
+ onUnavailable: () => void;
106
+ }) {
107
+ const [loaded, setLoaded] = useState<{ pdf: PDFDocumentProxy; width: number; height: number; labels: string[] | null } | null>(null),
108
+ [error, setError] = useState(""),
109
+ [started] = useState(() => Date.now()),
110
+ [now, setNow] = useState(() => Date.now());
111
+ useEffect(() => {
112
+ let live = true;
113
+ setLoaded(null);
114
+ setError("");
115
+ const task = pdfjs.getDocument({ url, enableXfa: false });
116
+ task.promise.then(
117
+ async (pdf) => {
118
+ const first = (await pdf.getPage(1)).getViewport({ scale: 1 }),
119
+ labels = await pdf.getPageLabels().catch(() => null);
120
+ if (live) setLoaded({ pdf, width: first.width, height: first.height, labels });
121
+ },
122
+ (failure: Error) => {
123
+ if (live) setError(failure.message);
124
+ },
125
+ );
126
+ return () => {
127
+ live = false;
128
+ void task.destroy();
129
+ };
130
+ }, [url]);
131
+ useEffect(() => {
132
+ if (loaded || error) return;
133
+ const timer = setInterval(() => setNow(Date.now()), 1000);
134
+ return () => clearInterval(timer);
135
+ }, [loaded, error]);
136
+ const paper = useMemo<Paper | null>(
137
+ () =>
138
+ loaded && {
139
+ pdf: loaded.pdf,
140
+ width: loaded.width,
141
+ height: loaded.height,
142
+ ...plan(loaded.pdf.numPages, loaded.labels, changes),
143
+ },
144
+ [loaded, changes],
145
+ );
146
+ if (error)
147
+ return (
148
+ <div className="paper-status" role="alert">
149
+ <p>{t("The full PDF could not be loaded.")}</p>
150
+ <button className="ghost" onClick={onUnavailable}>
151
+ {t("Changes")}
152
+ </button>
153
+ </div>
154
+ );
155
+ if (!paper)
156
+ return (
157
+ <div className="paper-status" role="status">
158
+ <div className="pulse" />
159
+ <p>
160
+ {t("Preparing the full PDF…")} <span className="paper-clock">{clock(now - started)}</span>
161
+ </p>
162
+ </div>
163
+ );
164
+ return (
165
+ <Pages paper={paper} render={render} scrollRoot={scrollRoot} onReady={onReady} />
166
+ );
167
+ }
168
+
169
+ function Pages({
170
+ paper,
171
+ render,
172
+ scrollRoot,
173
+ onReady,
174
+ }: {
175
+ paper: Paper;
176
+ render: (block: ChangeBlock, index: number) => ReactNode;
177
+ scrollRoot: RefObject<HTMLElement | null>;
178
+ onReady: () => void;
179
+ }) {
180
+ const box = useRef<HTMLDivElement>(null),
181
+ ready = useRef(onReady);
182
+ ready.current = onReady;
183
+ useEffect(() => paint(box.current!, paper, scrollRoot.current), [paper, scrollRoot]);
184
+ // The pages exist now: the selected change can be scrolled to.
185
+ useEffect(() => ready.current(), [paper]);
186
+ const slice = (page: number, top: number, bottom: number) =>
187
+ bottom - top > 0.5 && (
188
+ <div className="paper-context" key={`${page}:${top}`}>
189
+ <div
190
+ className="paper-slice"
191
+ data-top={top}
192
+ data-bottom={bottom}
193
+ style={{ aspectRatio: `${paper.width} / ${bottom - top}` }}
194
+ />
195
+ </div>
196
+ );
197
+ return (
198
+ <div className="paper" ref={box}>
199
+ {paper.before.map((c) => (
200
+ <Fragment key={c.index}>{render(c.block, c.index)}</Fragment>
201
+ ))}
202
+ {paper.pages.map((cuts, page) => {
203
+ let cursor = 0;
204
+ const parts: ReactNode[] = [];
205
+ for (const cut of cuts) {
206
+ parts.push(slice(page, cursor, cut.top));
207
+ for (const c of cut.shown) parts.push(<Fragment key={c.index}>{render(c.block, c.index)}</Fragment>);
208
+ cursor = cut.bottom;
209
+ }
210
+ parts.push(slice(page, cursor, paper.height));
211
+ return (
212
+ <section className="paper-page" data-page={page} key={page}>
213
+ {parts}
214
+ </section>
215
+ );
216
+ })}
217
+ </div>
218
+ );
219
+ }
220
+
221
+ // Paints each page into its slices while it is within about a screen and a
222
+ // half of the viewport, and frees the canvases again further away.
223
+ function paint(container: HTMLElement, paper: Paper, root: HTMLElement | null) {
224
+ const alive = new Map<number, { canvases: HTMLCanvasElement[]; task?: RenderTask }>(),
225
+ wanted = new Set<number>();
226
+ let running = 0,
227
+ width = container.clientWidth,
228
+ stopped = false;
229
+ const pages = () => Array.from(container.querySelectorAll<HTMLElement>(".paper-page"));
230
+ const release = (index: number) => {
231
+ const entry = alive.get(index);
232
+ if (!entry) return;
233
+ alive.delete(index);
234
+ entry.task?.cancel();
235
+ for (const canvas of entry.canvases) {
236
+ canvas.remove();
237
+ canvas.width = canvas.height = 0;
238
+ }
239
+ };
240
+ const pump = () => {
241
+ while (!stopped && running < 2) {
242
+ const middle = (root?.scrollTop ?? 0) + (root?.clientHeight ?? 0) / 2,
243
+ list = pages(),
244
+ next = [...wanted]
245
+ .filter((i) => !alive.has(i))
246
+ .sort((a, b) => Math.abs(list[a].offsetTop - middle) - Math.abs(list[b].offsetTop - middle))[0];
247
+ if (next === undefined) return;
248
+ void draw(next, list[next]);
249
+ }
250
+ };
251
+ const draw = async (index: number, element: HTMLElement) => {
252
+ running++;
253
+ const entry: { canvases: HTMLCanvasElement[]; task?: RenderTask } = { canvases: [] };
254
+ alive.set(index, entry);
255
+ try {
256
+ const page = await paper.pdf.getPage(index + 1);
257
+ if (alive.get(index) !== entry) return;
258
+ const slices = Array.from(element.querySelectorAll<HTMLElement>(".paper-slice")),
259
+ cssWidth = slices[0]?.clientWidth || element.clientWidth,
260
+ size = page.getViewport({ scale: 1 }),
261
+ scale = (cssWidth * Math.min(window.devicePixelRatio || 1, 3)) / size.width,
262
+ viewport = page.getViewport({ scale }),
263
+ source = document.createElement("canvas");
264
+ source.width = Math.floor(viewport.width);
265
+ source.height = Math.floor(viewport.height);
266
+ entry.task = page.render({ canvas: source, viewport });
267
+ await entry.task.promise;
268
+ if (alive.get(index) !== entry) return;
269
+ for (const slice of slices) {
270
+ const top = Math.round(Number(slice.dataset.top) * scale),
271
+ bottom = Math.min(source.height, Math.round(Number(slice.dataset.bottom) * scale));
272
+ if (bottom - top < 1) continue;
273
+ const canvas = document.createElement("canvas");
274
+ canvas.width = source.width;
275
+ canvas.height = bottom - top;
276
+ canvas.getContext("2d")!.drawImage(source, 0, top, source.width, bottom - top, 0, 0, source.width, bottom - top);
277
+ slice.append(canvas);
278
+ entry.canvases.push(canvas);
279
+ }
280
+ source.width = source.height = 0;
281
+ } catch (failure) {
282
+ if ((failure as Error)?.name !== "RenderingCancelledException") console.error(failure);
283
+ if (alive.get(index) === entry) alive.delete(index);
284
+ } finally {
285
+ running--;
286
+ pump();
287
+ }
288
+ };
289
+ const observer = new IntersectionObserver(
290
+ (entries) => {
291
+ for (const entry of entries) {
292
+ const index = Number((entry.target as HTMLElement).dataset.page);
293
+ if (entry.isIntersecting) wanted.add(index);
294
+ else {
295
+ wanted.delete(index);
296
+ release(index);
297
+ }
298
+ }
299
+ pump();
300
+ },
301
+ { root, rootMargin: "1200px 0px" },
302
+ );
303
+ pages().forEach((page) => observer.observe(page));
304
+ // A wider column needs sharper pages.
305
+ const resized = new ResizeObserver(() => {
306
+ if (Math.abs(container.clientWidth - width) < 1) return;
307
+ width = container.clientWidth;
308
+ for (const index of [...alive.keys()]) release(index);
309
+ pump();
310
+ });
311
+ resized.observe(container);
312
+ return () => {
313
+ stopped = true;
314
+ observer.disconnect();
315
+ resized.disconnect();
316
+ for (const index of [...alive.keys()]) release(index);
317
+ };
318
+ }
@@ -0,0 +1,36 @@
1
+ import { supportedLanguage, singularCount, interpolateMessage, type Language } from '../locales.ts';
2
+ import { messages } from './locales/index.ts';
3
+ export type { Language };
4
+
5
+ export function pageLanguage(search = location.search): Language {
6
+ return supportedLanguage(new URLSearchParams(search).get('lang')) || 'en';
7
+ }
8
+ const current: Language =
9
+ typeof location === "undefined" ? "en" : pageLanguage();
10
+ if (typeof document !== "undefined") document.documentElement.lang = current;
11
+
12
+ export const language = () => current;
13
+ // Dates and numbers follow the interface language.
14
+ export const locale = () => current;
15
+
16
+ // Links inside the viewer keep the page's language.
17
+ export function withLanguage(href: string) {
18
+ return current === "en"
19
+ ? href
20
+ : `${href}${href.includes("?") ? "&" : "?"}lang=${current}`;
21
+ }
22
+
23
+ // t("Change {n}", { n }) fills {placeholders} after translating.
24
+ export function t(text: string, values?: Record<string, string | number>) {
25
+ const template = Object.hasOwn(messages[current], text) ? messages[current][text] : text;
26
+ return interpolateMessage(template, current, values);
27
+ }
28
+ // Select the count form using the active locale; {n} is the count.
29
+ export function tn(
30
+ count: number,
31
+ one: string,
32
+ other: string,
33
+ values?: Record<string, string | number>,
34
+ ) {
35
+ return t(singularCount(current, count) ? one : other, { n: count, ...values });
36
+ }
@@ -0,0 +1,253 @@
1
+ {
2
+ "(no such paper)": "(kein solcher Artikel)",
3
+ "(not registered)": "(nicht registriert)",
4
+ "Accept": "Annehmen",
5
+ "Accept all": "Alle annehmen",
6
+ "Accept all ({n})": "Alle annehmen ({n})",
7
+ "Accept anyway": "Trotzdem übernehmen",
8
+ "Accept bibliography entries that are not verified?": "Nicht verifizierte Einträge im Literaturverzeichnis übernehmen?",
9
+ "Accept {n} undecided change": "{n} unentschiedene Änderung annehmen",
10
+ "Accept {n} undecided changes": "{n} unentschiedene Änderungen annehmen",
11
+ "Accepted": "Angenommen",
12
+ "Accepted changes are in your manuscript.": "Angenommene Änderungen befinden sich in Ihrem Manuskript.",
13
+ "Affects {n} place": "Betrifft {n} Stelle",
14
+ "Affects {n} places": "Betrifft {n} Stellen",
15
+ "After": "Nachher",
16
+ "Agent needs your input": "Agent benötigt Ihre Eingabe",
17
+ "All changes are staged": "Alle Änderungen sind vorgemerkt",
18
+ "All changes are staged.": "Alle Änderungen sind vorgemerkt.",
19
+ "All changes reviewed": "Alle Änderungen überprüft",
20
+ "All changes reviewed. Your decisions are saved.": "Alle Änderungen überprüft. Ihre Entscheidungen sind gespeichert.",
21
+ "All decided": "Alle entschieden",
22
+ "All reviews": "Alle Überprüfungen",
23
+ "All tasks": "Alle Aufgaben",
24
+ "An agent can write an entry for a work that does not exist, or with the wrong authors, year or DOI. Check each one before it goes into your paper.": "Ein Agent kann einen Eintrag für ein nicht existierendes Werk oder mit falschen Autoren, falschem Jahr oder falschem DOI erstellen. Prüfen Sie jeden Eintrag, bevor er in Ihren Artikel übernommen wird.",
25
+ "Another entry in the bibliography has the same key.": "Ein anderer Eintrag im Literaturverzeichnis hat denselben Schlüssel.",
26
+ "Authors": "Autoren",
27
+ "Awaiting revision": "Wartet auf Überarbeitung",
28
+ "Awaiting your review": "Wartet auf Ihre Überprüfung",
29
+ "Back to current changes →": "Zurück zu den aktuellen Änderungen →",
30
+ "Back to review": "Zurück zur Überprüfung",
31
+ "Before": "Vorher",
32
+ "BibTeX cannot read this entry.": "BibTeX kann diesen Eintrag nicht lesen.",
33
+ "Bibliography": "Literaturverzeichnis",
34
+ "Binary file changed.": "Binärdatei geändert.",
35
+ "Cancel": "Abbrechen",
36
+ "Change map": "Änderungsübersicht",
37
+ "Change {list}": "Änderung {list}",
38
+ "Change {n}": "Änderung {n}",
39
+ "Changed entry": "Geänderter Eintrag",
40
+ "Changes": "Änderungen",
41
+ "Changes are ready": "Änderungen sind bereit",
42
+ "Changes {list}": "Änderungen {list}",
43
+ "Check again": "Erneut prüfen",
44
+ "Checked by you": "Von Ihnen geprüft",
45
+ "Checking…": "Wird geprüft…",
46
+ "Choose how to save the accepted changes to your manuscript.": "Wählen Sie aus, wie die angenommenen Änderungen in Ihrem Manuskript gespeichert werden sollen.",
47
+ "Cited {n} time: {places}": "{n}-mal zitiert: {places}",
48
+ "Cited {n} times: {places}": "{n}-mal zitiert: {places}",
49
+ "Cites {key}, which no bibliography entry defines.": "Zitiert {key}, was in keinem Eintrag im Literaturverzeichnis definiert ist.",
50
+ "Clipboard unavailable. Select the PDF text and use your browser’s Copy command.": "Zwischenablage nicht verfügbar. Wählen Sie den PDF-Text aus und verwenden Sie den Kopierbefehl Ihres Browsers.",
51
+ "Close": "Schließen",
52
+ "Closest record": "Ähnlichster Datensatz",
53
+ "Comment": "Kommentieren",
54
+ "Comment on change {n}": "Kommentar zu Änderung {n}",
55
+ "Commented": "Kommentiert",
56
+ "Commit them when you are ready.": "Committen Sie sie, sobald Sie bereit sind.",
57
+ "Comparing with the last commit…": "Vergleiche mit dem letzten Commit…",
58
+ "Comparison failed": "Vergleich fehlgeschlagen",
59
+ "Compiling LaTeX": "LaTeX wird kompiliert",
60
+ "Continued: only line breaks moved here.": "Fortsetzung: Hier wurden nur Zeilenumbrüche verschoben.",
61
+ "Copied selected text with file, lines and PDF page.": "Ausgewählter Text mit Datei, Zeilen und PDF-Seite kopiert.",
62
+ "Copied the change location.": "Position der Änderung kopiert.",
63
+ "Copy reference": "Referenz kopieren",
64
+ "Copy selected PDF text": "Ausgewählten PDF-Text kopieren",
65
+ "Copy selected text with this change’s file, line numbers and PDF page": "Ausgewählten Text mit Datei, Zeilennummern und PDF-Seite dieser Änderung kopieren",
66
+ "Could not check": "Prüfung nicht möglich",
67
+ "Current changes": "Aktuelle Änderungen",
68
+ "DOI": "DOI",
69
+ "Differs from the record": "Weicht vom Datensatz ab",
70
+ "Earlier changes": "Frühere Änderungen",
71
+ "Edited": "Bearbeitet",
72
+ "Editing": "In Bearbeitung",
73
+ "Entry": "Eintrag",
74
+ "Feedback queued": "Feedback in Warteschlange",
75
+ "Feedback sent · Agent is working": "Feedback gesendet · Agent arbeitet",
76
+ "Filter reviews": "Überprüfungen filtern",
77
+ "Finish or abort the merge in the Git panel before staging or reverting changes.": "Schließen Sie den Merge im Git-Bereich ab oder brechen Sie ihn ab, bevor Sie Änderungen vormerken oder verwerfen.",
78
+ "Finished": "Abgeschlossen",
79
+ "History": "Verlauf",
80
+ "I checked it myself": "Ich habe es selbst geprüft",
81
+ "In review": "In Überprüfung",
82
+ "J / K or → / ←": "J / K oder → / ←",
83
+ "Keyboard shortcuts": "Tastenkürzel",
84
+ "Last decision undone. The restored changes need review again.": "Letzte Entscheidung rückgängig gemacht. Die wiederhergestellten Änderungen erfordern eine erneute Überprüfung.",
85
+ "Layout": "Layout",
86
+ "Leaves {n} change with bibliography entries to check.": "Lässt {n} Änderung mit zu prüfenden Literaturverzeichniseinträgen übrig.",
87
+ "Leaves {n} changes with bibliography entries to check.": "Lässt {n} Änderungen mit zu prüfenden Literaturverzeichniseinträgen übrig.",
88
+ "Loading…": "Wird geladen…",
89
+ "Long change: the source is shortened.": "Umfangreiche Änderung: Der Quelltext ist gekürzt.",
90
+ "Looking this entry up in Crossref, DBLP and arXiv…": "Dieser Eintrag wird in Crossref, DBLP und arXiv gesucht…",
91
+ "Matches the record in {source}, apart from the small differences below.": "Stimmt bis auf die kleinen Abweichungen unten mit dem Datensatz in {source} überein.",
92
+ "Matches the record in {source}.": "Stimmt mit dem Datensatz in {source} überein.",
93
+ "Merge review": "Überprüfung zusammenführen",
94
+ "Mixed": "Gemischt",
95
+ "Moved": "Verschoben",
96
+ "New changes will appear here when ready.": "Neue Änderungen werden hier angezeigt, sobald sie bereit sind.",
97
+ "New entry": "Neuer Eintrag",
98
+ "Next / previous change": "Nächste / vorherige Änderung",
99
+ "Next change": "Nächste Änderung",
100
+ "Next change (J or →)": "Nächste Änderung (J oder →)",
101
+ "Next undecided": "Nächste unentschiedene",
102
+ "Next unstaged": "Nächste nicht vorgemerkte Änderung",
103
+ "No arXiv paper has this identifier, and no record matches the title.": "Kein arXiv-Artikel hat diese Kennung und kein Datensatz stimmt mit dem Titel überein.",
104
+ "No bibliography entry has this key.": "Kein Eintrag im Literaturverzeichnis hat diesen Schlüssel.",
105
+ "No changes to review.": "Keine Änderungen zur Überprüfung.",
106
+ "No open reviews yet. Start a task from your document repository.": "Noch keine offenen Überprüfungen. Starten Sie eine Aufgabe aus Ihrem Dokument-Repository.",
107
+ "No record in Crossref, DBLP or arXiv matches this entry. Theses, reports and many books are in none of them, so check it yourself.": "Kein Datensatz in Crossref, DBLP oder arXiv stimmt mit diesem Eintrag überein. Abschlussarbeiten, Berichte und viele Bücher sind dort nicht verzeichnet – prüfen Sie ihn daher selbst.",
108
+ "No reviews match this filter.": "Keine Überprüfungen entsprechen diesem Filter.",
109
+ "No uncommitted changes": "Keine nicht committeten Änderungen",
110
+ "No visible change": "Keine sichtbare Änderung",
111
+ "No visible change in the PDF; see the LaTeX source.": "Keine sichtbare Änderung im PDF; siehe LaTeX-Quelltext.",
112
+ "Not checked online": "Nicht online geprüft",
113
+ "Not cited in the paper, so it does not appear in the PDF.": "Nicht im Artikel zitiert, erscheint daher nicht im PDF.",
114
+ "Not published yet": "Noch nicht veröffentlicht",
115
+ "Not verified": "Nicht verifiziert",
116
+ "Nothing to check": "Nichts zu prüfen",
117
+ "Online checks are off in Settings, so this entry was not looked up.": "Online-Prüfungen sind in den Einstellungen deaktiviert, daher wurde dieser Eintrag nicht gesucht.",
118
+ "Open Chat to answer the agent's question.": "Öffnen Sie den Chat, um die Frage des Agenten zu beantworten.",
119
+ "Open →": "Öffnen →",
120
+ "Other changes": "Weitere Änderungen",
121
+ "Partly staged": "Teilweise vorgemerkt",
122
+ "Pause this task in Papilot before accepting, rejecting, or undoing changes. You can still add comments.": "Pausieren Sie diese Aufgabe in Papilot, bevor Sie Änderungen annehmen, ablehnen oder rückgängig machen. Sie können weiterhin Kommentare hinzufügen.",
123
+ "Pending": "Ausstehend",
124
+ "Preparing the full PDF…": "Vollständiges PDF wird vorbereitet…",
125
+ "Preparing the next review…": "Nächste Überprüfung wird vorbereitet…",
126
+ "Previous change": "Vorherige Änderung",
127
+ "Previous change (K or ←)": "Vorherige Änderung (K oder ←)",
128
+ "Publish failed": "Veröffentlichung fehlgeschlagen",
129
+ "Publishing": "Wird veröffentlicht",
130
+ "Put back what the last revert removed (U)": "Wiederherstellen, was beim letzten Verwerfen entfernt wurde (U)",
131
+ "Queued behind {n} updates…": "In Warteschlange hinter {n} Aktualisierungen…",
132
+ "Queued behind {n} update…": "In Warteschlange hinter {n} Aktualisierung…",
133
+ "Queued…": "In Warteschlange…",
134
+ "Reconnecting…": "Verbindung wird wiederhergestellt…",
135
+ "Record": "Datensatz",
136
+ "Reflow": "Textumbruch",
137
+ "Reject": "Ablehnen",
138
+ "Reject all": "Alle ablehnen",
139
+ "Reject all ({n})": "Alle ablehnen ({n})",
140
+ "Reject {n} undecided change": "{n} unentschiedene Änderung ablehnen",
141
+ "Reject {n} undecided changes": "{n} unentschiedene Änderungen ablehnen",
142
+ "Rejected": "Abgelehnt",
143
+ "Removed entry": "Entfernter Eintrag",
144
+ "Rendering the diff": "Diff wird gerendert",
145
+ "Revert": "Zurücksetzen",
146
+ "Revert undone. The changes are back in your files.": "Verwerfen rückgängig gemacht. Die Änderungen sind wieder in Ihren Dateien.",
147
+ "Reverted": "Zurückgesetzt",
148
+ "Review complete": "Überprüfung abgeschlossen",
149
+ "Review history": "Überprüfungsverlauf",
150
+ "Review the current change": "Aktuelle Änderung überprüfen",
151
+ "Review view": "Überprüfungsansicht",
152
+ "Review →": "Überprüfen →",
153
+ "Reviewed changes · View in History": "Überprüfte Änderungen · Im Verlauf ansehen",
154
+ "Revised after your comment: ": "Nach Ihrem Kommentar überarbeitet: ",
155
+ "Search by title or task name": "Nach Titel oder Aufgabenname suchen",
156
+ "Search reviews": "Überprüfungen durchsuchen",
157
+ "Select Check status in Papilot before accepting, rejecting, or undoing changes. You can still add comments.": "Wählen Sie in Papilot „Status prüfen“, bevor Sie Änderungen annehmen, ablehnen oder rückgängig machen. Sie können weiterhin Kommentare hinzufügen.",
158
+ "Select text directly in the PDF preview. Copy reference includes the selection and its file, line numbers and page. Review shortcuts pause while text is selected.": "Wählen Sie Text direkt in der PDF-Vorschau aus. „Referenz kopieren“ übernimmt die Auswahl samt Datei, Zeilennummern und Seite. Tastenkürzel für die Überprüfung sind pausiert, solange Text ausgewählt ist.",
159
+ "Send": "Senden",
160
+ "Show LaTeX source": "LaTeX-Quelltext anzeigen",
161
+ "Show the LaTeX source (S)": "LaTeX-Quelltext anzeigen (S)",
162
+ "Show {n} more": "{n} weitere anzeigen",
163
+ "Since the last commit": "Seit dem letzten Commit",
164
+ "Source only": "Nur Quelltext",
165
+ "Stage": "Vormerken",
166
+ "Stage all": "Alle vormerken",
167
+ "Stage all ({n})": "Alle vormerken ({n})",
168
+ "Stage anyway": "Trotzdem vormerken",
169
+ "Stage bibliography entries that are not verified?": "Nicht verifizierte Einträge im Literaturverzeichnis vormerken?",
170
+ "Stage or unstage": "Vormerken oder Vormerkung aufheben",
171
+ "Stage the {n} unstaged change.": "Die {n} nicht vorgemerkte Änderung vormerken.",
172
+ "Stage the {n} unstaged changes.": "Die {n} nicht vorgemerkten Änderungen vormerken.",
173
+ "Staged": "Vorgemerkt",
174
+ "Starting": "Wird gestartet",
175
+ "Taking a snapshot": "Snapshot wird erstellt",
176
+ "Text": "Text",
177
+ "The DOI is not registered, and no record matches the title.": "Der DOI ist nicht registriert und kein Datensatz stimmt mit dem Titel überein.",
178
+ "The PDF matches the last commit.": "Das PDF stimmt mit dem letzten Commit überein.",
179
+ "The comparison could not be built: ": "Der Vergleich konnte nicht erstellt werden: ",
180
+ "The entry has no title, DOI or arXiv identifier to look up.": "Der Eintrag enthält weder Titel noch DOI oder arXiv-Kennung für die Suche.",
181
+ "The full PDF could not be loaded.": "Das vollständige PDF konnte nicht geladen werden.",
182
+ "The last commit changed. The comparison is being updated.": "Der letzte Commit hat sich geändert. Der Vergleich wird aktualisiert.",
183
+ "The latest publish failed: ": "Die letzte Veröffentlichung ist fehlgeschlagen: ",
184
+ "The paper does not use this .bib file, so the entry will not appear.": "Der Artikel verwendet diese .bib-Datei nicht, daher wird der Eintrag nicht angezeigt.",
185
+ "The paper still cites this key, and no entry defines it now.": "Der Artikel zitiert diesen Schlüssel noch, aber kein Eintrag definiert ihn mehr.",
186
+ "The sources could not be reached.": "Die Quellen konnten nicht erreicht werden.",
187
+ "There are no accepted changes to save.": "Es gibt keine angenommenen Änderungen zum Speichern.",
188
+ "There are no new changes to review. Previous decisions are available in History.": "Es gibt keine neuen Änderungen zur Überprüfung. Frühere Entscheidungen sind im Verlauf verfügbar.",
189
+ "This page updates by itself.": "Diese Seite aktualisiert sich automatisch.",
190
+ "This review is finished. Accepted changes are in your manuscript.": "Diese Überprüfung ist abgeschlossen. Angenommene Änderungen befinden sich in Ihrem Manuskript.",
191
+ "This task is being deleted. Review actions and comments are unavailable.": "Diese Aufgabe wird gelöscht. Überprüfungsaktionen und Kommentare sind nicht verfügbar.",
192
+ "This task is no longer available in Papilot. Return to the workspace.": "Diese Aufgabe ist in Papilot nicht mehr verfügbar. Kehren Sie zum Arbeitsbereich zurück.",
193
+ "Title": "Titel",
194
+ "Unchanged text": "Unveränderter Text",
195
+ "Unchanged text is folded": "Unveränderter Text ist eingeklappt",
196
+ "Uncommitted changes": "Nicht committete Änderungen",
197
+ "Undo": "Rückgängig",
198
+ "Undo last decision": "Letzte Entscheidung rückgängig machen",
199
+ "Undo revert": "Verwerfen rückgängig machen",
200
+ "Undo {action} · {changes}": "{action} rückgängig machen · {changes}",
201
+ "Unstage": "Vormerkung aufheben",
202
+ "Unstaged": "Nicht vorgemerkt",
203
+ "Updating changes…": "Änderungen werden aktualisiert…",
204
+ "Updating the comparison with your latest edits. You can keep staging and reverting meanwhile.": "Der Vergleich wird mit Ihren neuesten Bearbeitungen aktualisiert. Sie können währenddessen weiter Änderungen vormerken und verwerfen.",
205
+ "Verified": "Verifiziert",
206
+ "Viewing earlier changes.": "Frühere Änderungen werden angezeigt.",
207
+ "Wait for the PDF build before accepting, rejecting, or undoing changes. You can still add comments.": "Warten Sie auf die PDF-Erstellung, bevor Sie Änderungen annehmen, ablehnen oder rückgängig machen. Sie können weiterhin Kommentare hinzufügen.",
208
+ "Wait for the current review action before accepting, rejecting, or undoing changes. You can still add comments.": "Warten Sie auf die aktuelle Überprüfungsaktion, bevor Sie Änderungen annehmen, ablehnen oder rückgängig machen. Sie können weiterhin Kommentare hinzufügen.",
209
+ "Wait for the merge suggestion before changing review decisions.": "Warten Sie auf den Zusammenführungsvorschlag, bevor Sie Überprüfungsentscheidungen ändern.",
210
+ "Waiting for the agent to publish its changes.": "Warten auf die Veröffentlichung der Änderungen durch den Agenten.",
211
+ "What should the agent change here?": "Was soll der Agent hier ändern?",
212
+ "Year": "Jahr",
213
+ "You checked this entry yourself.": "Sie haben diesen Eintrag selbst geprüft.",
214
+ "Your comment: ": "Ihr Kommentar: ",
215
+ "Your comments are saved for the agent.": "Ihre Kommentare sind für den Agenten gespeichert.",
216
+ "Your comments are saved. Send feedback so the agent can revise.": "Ihre Kommentare sind gespeichert. Senden Sie Feedback, damit der Agent überarbeiten kann.",
217
+ "Your decisions are saved. Send them to the agent to continue.": "Ihre Entscheidungen sind gespeichert. Senden Sie diese an den Agenten, um fortzufahren.",
218
+ "Your manuscript is unchanged until you save the merged result.": "Ihr Manuskript bleibt unverändert, bis Sie das zusammengeführte Ergebnis speichern.",
219
+ "arXiv": "arXiv",
220
+ "checking {n}…": "{n} wird geprüft…",
221
+ "deleted": "gelöscht",
222
+ "lines {start}–{end}": "Zeilen {start}–{end}",
223
+ "new file": "neue Datei",
224
+ "online checks are off": "Online-Prüfungen sind deaktiviert",
225
+ "p. {pages}": "S. {pages}",
226
+ "small": "geringfügig",
227
+ "with {changes}": "mit {changes}",
228
+ "{decided} of {total} decided": "{decided} von {total} entschieden",
229
+ "{label}, PDF page {page}": "{label}, PDF-Seite {page}",
230
+ "{n} accepted": "{n} angenommen",
231
+ "{n} checked by you": "{n} von Ihnen geprüft",
232
+ "{n} citation without an entry": "{n} Zitat ohne Eintrag",
233
+ "{n} citations without an entry": "{n} Zitate ohne Eintrag",
234
+ "{n} commented": "{n} kommentiert",
235
+ "{n} could not be checked": "{n} nicht prüfbar",
236
+ "{n} differ from their records": "{n} weichen vom Datensatz ab",
237
+ "{n} differs from its record": "{n} weicht vom Datensatz ab",
238
+ "{n} edits · LaTeX": "{n} Bearbeitungen · LaTeX",
239
+ "{n} entries changed": "{n} Einträge geändert",
240
+ "{n} entry changed": "{n} Eintrag geändert",
241
+ "{n} new LaTeX warning": "{n} neue LaTeX-Warnung",
242
+ "{n} new LaTeX warnings": "{n} neue LaTeX-Warnungen",
243
+ "{n} not verified": "{n} nicht verifiziert",
244
+ "{n} open review": "{n} offene Überprüfung",
245
+ "{n} open reviews": "{n} offene Überprüfungen",
246
+ "{n} pending": "{n} ausstehend",
247
+ "{n} rejected": "{n} abgelehnt",
248
+ "{n} verified": "{n} verifiziert",
249
+ "{sources} could not be reached.": "{sources} konnte nicht erreicht werden.",
250
+ "{source} has this work with other details. Compare them below.": "In {source} ist dieses Werk mit abweichenden Angaben verzeichnet. Vergleichen Sie diese unten.",
251
+ "{staged} of {total} staged": "{staged} von {total} vorgemerkt",
252
+ "⌘↵ send · Esc cancel": "⌘↵ senden · Esc abbrechen"
253
+ }