@anchrd/intel-ui 0.40.0 → 0.42.0

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.
@@ -0,0 +1,342 @@
1
+ import FileViewer, {
2
+ type FileViewerHandle,
3
+ type ViewerOptions,
4
+ type ViewerState,
5
+ } from "@file-viewer/react";
6
+ import {
7
+ ChevronLeft,
8
+ ChevronRight,
9
+ Minus,
10
+ PanelLeft,
11
+ Plus,
12
+ RotateCcw,
13
+ RotateCw,
14
+ Search,
15
+ X,
16
+ } from "lucide-react";
17
+ import { useEffect, useMemo, useRef, useState } from "react";
18
+ import { ActionSlot } from "@/app/action-slot/action-slot.tsx";
19
+ import { useI18n } from "@/i18n/i18n-context.tsx";
20
+ import { useResolvedTheme } from "@/theme/theme-context.tsx";
21
+
22
+ // The PDF renderer exposes no stable Shadow Parts for its navigation internals. These selectors
23
+ // intentionally target the pinned @file-viewer/renderer-pdf 2.3.0 DOM; verify them before upgrades.
24
+ const viewerTheme = `
25
+ :host,.file-viewer-web-shell{
26
+ --file-viewer-render-surface-background:var(--muted);
27
+ --file-viewer-bg:var(--background);--file-viewer-content-bg:var(--muted);
28
+ --file-viewer-text:var(--foreground);--file-viewer-muted:var(--muted-foreground);
29
+ --file-viewer-border:var(--border);--file-viewer-toolbar-bg:var(--background);
30
+ --file-viewer-toolbar-border:var(--border);--file-viewer-group-bg:var(--muted);
31
+ --file-viewer-group-border:var(--border);--file-viewer-button-color:var(--foreground);
32
+ --file-viewer-button-hover-bg:var(--accent);--file-viewer-button-hover-color:var(--accent-foreground);
33
+ --file-viewer-button-disabled-color:var(--muted-foreground);--file-viewer-input-bg:var(--background);
34
+ --file-viewer-input-color:var(--foreground);--file-viewer-focus-ring:var(--ring);
35
+ }
36
+ .pdf-shell,.pdf-wrapper{background:var(--muted)!important;color:var(--foreground)!important}
37
+ .pdf-nav-pane,.pdf-nav-tabs,.pdf-nav-head{background:var(--background)!important;border-color:var(--border)!important}
38
+ .pdf-nav-head{display:none!important}.pdf-nav-tabs{display:flex!important;justify-content:flex-end;padding:6px!important}
39
+ .pdf-nav-tabs button{width:30px;min-width:30px;padding:0;font-size:0;color:var(--muted-foreground)!important}
40
+ .pdf-nav-tabs button svg{width:15px;height:15px;margin:auto;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
41
+ .pdf-nav-tabs button:hover,.pdf-nav-tabs button.active{background:var(--accent)!important;border-color:var(--border)!important;color:var(--accent-foreground)!important}
42
+ .pdf-page-list{gap:4px!important;padding:6px!important}.pdf-page-button{display:flex!important;min-height:36px!important;padding:4px!important;border-color:transparent!important;background:transparent!important;color:var(--foreground)!important}
43
+ .pdf-page-button:hover,.pdf-page-button--active{background:var(--accent)!important;border-color:var(--border)!important;box-shadow:none!important}
44
+ .pdf-page-thumb{width:28px!important;height:28px!important;border:0!important;background:transparent!important;color:var(--foreground)!important}
45
+ .pdf-page-label{display:none!important}.pdf-outline-button{color:var(--foreground)!important}.pdf-outline-button:hover{background:var(--accent)!important;border-color:var(--border)!important}
46
+ `;
47
+
48
+ export function FilePreviewView({
49
+ file,
50
+ renderer,
51
+ pdf = false,
52
+ }: {
53
+ file: File;
54
+ renderer: unknown;
55
+ pdf?: boolean;
56
+ }) {
57
+ const theme = useResolvedTheme();
58
+ const i18n = useI18n();
59
+ const viewer = useRef<FileViewerHandle>(null);
60
+ const searchInput = useRef<HTMLInputElement>(null);
61
+ const [state, setState] = useState<ViewerState | null>(null);
62
+ const [searchOpen, setSearchOpen] = useState(false);
63
+ const [query, setQuery] = useState("");
64
+ const options = useMemo<ViewerOptions>(
65
+ () => ({
66
+ renderers: [renderer] as unknown as NonNullable<ViewerOptions["renderers"]>,
67
+ rendererMode: "replace",
68
+ builtinRenderers: "none",
69
+ autoRenderers: false,
70
+ styleIsolation: "shadow",
71
+ theme,
72
+ locale: "auto",
73
+ toolbar: false,
74
+ ...(pdf
75
+ ? { pdf: { toolbar: false, navigation: true, defaultNavigationVisible: false } }
76
+ : {}),
77
+ ui: { density: "compact", surfaceBackground: "transparent" },
78
+ }),
79
+ [pdf, renderer, theme],
80
+ );
81
+ const viewState = state?.viewState;
82
+ const page = viewState?.page ?? 1;
83
+ const pageCount = viewState?.pageCount ?? 0;
84
+ const scale = state?.zoom?.scale ?? viewState?.scale ?? 1;
85
+ const search = state?.search;
86
+
87
+ useEffect(() => {
88
+ if (!state?.ready) return;
89
+ const root = viewer.current?.getController()?.container.shadowRoot;
90
+ if (!root || root.querySelector("style[data-intel-viewer-theme]")) return;
91
+ const style = document.createElement("style");
92
+ style.dataset.intelViewerTheme = "true";
93
+ style.textContent = viewerTheme;
94
+ root.append(style);
95
+ const labelPages = () => {
96
+ for (const button of root.querySelectorAll<HTMLButtonElement>(".pdf-page-button")) {
97
+ const label = button.querySelector(".pdf-page-label")?.textContent?.trim();
98
+ if (label) button.title = label;
99
+ }
100
+ for (const [index, button] of root
101
+ .querySelectorAll<HTMLButtonElement>(".pdf-nav-tabs button")
102
+ .entries()) {
103
+ if (button.querySelector("[data-intel-navigation-icon]")) continue;
104
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
105
+ svg.dataset.intelNavigationIcon = "true";
106
+ svg.setAttribute("viewBox", "0 0 24 24");
107
+ svg.setAttribute("aria-hidden", "true");
108
+ const paths =
109
+ index === 0
110
+ ? ["M8 6h13M8 12h13M8 18h13", "M3 6h.01M3 12h.01M3 18h.01"]
111
+ : ["M4 6h7M9 12h11M13 18h7", "M4 6v12h9M9 6v6M13 12v6"];
112
+ for (const pathData of paths) {
113
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
114
+ path.setAttribute("d", pathData);
115
+ svg.append(path);
116
+ }
117
+ button.prepend(svg);
118
+ }
119
+ };
120
+ labelPages();
121
+ const observer = new MutationObserver(labelPages);
122
+ observer.observe(root, { childList: true, subtree: true });
123
+ return () => observer.disconnect();
124
+ }, [state?.ready]);
125
+
126
+ useEffect(() => {
127
+ if (searchOpen) searchInput.current?.focus();
128
+ }, [searchOpen]);
129
+
130
+ const applyViewState = (next: Record<string, unknown>) =>
131
+ viewer.current?.applyViewState({ ...viewState, ...next });
132
+
133
+ return (
134
+ <div className="relative h-full min-h-64">
135
+ {pdf ? (
136
+ <ActionSlot name="title-actions">
137
+ <button
138
+ type="button"
139
+ aria-label={i18n.t("attachment.viewer.navigation")}
140
+ aria-pressed={viewState?.navigation?.visible ?? false}
141
+ onClick={() =>
142
+ void applyViewState({
143
+ navigation: {
144
+ ...viewState?.navigation,
145
+ visible: !(viewState?.navigation?.visible ?? false),
146
+ },
147
+ })
148
+ }
149
+ className="inline-flex size-8 items-center justify-center rounded-md bg-transparent outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring aria-pressed:bg-muted"
150
+ >
151
+ <PanelLeft aria-hidden="true" className="size-4" />
152
+ </button>
153
+ </ActionSlot>
154
+ ) : null}
155
+ <div className="absolute inset-x-0 top-0 bottom-14 min-h-0">
156
+ <FileViewer
157
+ ref={viewer}
158
+ file={file}
159
+ className="h-full min-h-0"
160
+ options={options}
161
+ onStateChange={setState}
162
+ />
163
+ </div>
164
+ {state?.ready ? (
165
+ <div
166
+ role="toolbar"
167
+ aria-label={i18n.t("attachment.viewer.controls")}
168
+ className="absolute inset-x-3 bottom-2 z-[5] mx-auto flex min-h-10 w-fit max-w-[calc(100%-1.5rem)] items-center gap-1 rounded-full border bg-background/95 p-1 shadow-lg backdrop-blur"
169
+ >
170
+ <span role="status" aria-live="polite" aria-atomic="true" className="sr-only">
171
+ {search?.query
172
+ ? search.total > 0
173
+ ? i18n.t("attachment.viewer.searchResults", {
174
+ current: search.currentIndex + 1,
175
+ total: search.total,
176
+ })
177
+ : i18n.t("attachment.viewer.searchNoResults")
178
+ : ""}
179
+ </span>
180
+ {pdf && pageCount > 0 && !searchOpen ? (
181
+ <div className="flex items-center rounded-full bg-muted p-0.5">
182
+ <button
183
+ type="button"
184
+ aria-label={i18n.t("attachment.viewer.previousPage")}
185
+ disabled={page <= 1}
186
+ onClick={() => void applyViewState({ page: page - 1 })}
187
+ className="size-7 rounded-full hover:bg-accent disabled:opacity-40"
188
+ >
189
+ <ChevronLeft aria-hidden="true" className="mx-auto size-3.5" />
190
+ </button>
191
+ <span className="min-w-14 px-1 text-center text-xs tabular-nums">
192
+ {page} / {pageCount}
193
+ </span>
194
+ <button
195
+ type="button"
196
+ aria-label={i18n.t("attachment.viewer.nextPage")}
197
+ disabled={page >= pageCount}
198
+ onClick={() => void applyViewState({ page: page + 1 })}
199
+ className="size-7 rounded-full hover:bg-accent disabled:opacity-40"
200
+ >
201
+ <ChevronRight aria-hidden="true" className="mx-auto size-3.5" />
202
+ </button>
203
+ </div>
204
+ ) : null}
205
+ {searchOpen ? (
206
+ <form
207
+ className="flex min-w-0 items-center gap-1"
208
+ onSubmit={(event) => {
209
+ event.preventDefault();
210
+ void viewer.current?.searchDocument(query);
211
+ }}
212
+ >
213
+ <input
214
+ ref={searchInput}
215
+ value={query}
216
+ onChange={(event) => setQuery(event.target.value)}
217
+ placeholder={i18n.t("attachment.viewer.searchPlaceholder")}
218
+ className="h-7 w-28 min-w-0 rounded-full border bg-background px-3 text-xs outline-none focus:ring-2 focus:ring-ring sm:w-40"
219
+ />
220
+ <button
221
+ type="submit"
222
+ aria-label={i18n.t("attachment.viewer.search")}
223
+ className="grid size-7 place-items-center rounded-full hover:bg-accent"
224
+ >
225
+ <Search aria-hidden="true" className="size-3.5" />
226
+ </button>
227
+ {search && search.total > 0 ? (
228
+ <div className="flex items-center rounded-full bg-muted p-0.5">
229
+ <button
230
+ type="button"
231
+ aria-label={i18n.t("attachment.viewer.previousResult")}
232
+ disabled={search.total <= 1}
233
+ onClick={() => void viewer.current?.previousSearchResult()}
234
+ className="grid size-7 place-items-center rounded-full hover:bg-accent disabled:opacity-40"
235
+ >
236
+ <ChevronLeft aria-hidden="true" className="size-3.5" />
237
+ </button>
238
+ <span
239
+ aria-hidden="true"
240
+ className="min-w-10 px-1 text-center text-xs tabular-nums"
241
+ >
242
+ {search.currentIndex + 1} / {search.total}
243
+ </span>
244
+ <button
245
+ type="button"
246
+ aria-label={i18n.t("attachment.viewer.nextResult")}
247
+ disabled={search.total <= 1}
248
+ onClick={() => void viewer.current?.nextSearchResult()}
249
+ className="grid size-7 place-items-center rounded-full hover:bg-accent disabled:opacity-40"
250
+ >
251
+ <ChevronRight aria-hidden="true" className="size-3.5" />
252
+ </button>
253
+ </div>
254
+ ) : null}
255
+ {search?.query && search.total === 0 ? (
256
+ <span aria-hidden="true" className="min-w-10 px-1 text-center text-xs tabular-nums">
257
+ {i18n.t("attachment.viewer.searchNoResults")}
258
+ </span>
259
+ ) : null}
260
+ <button
261
+ type="button"
262
+ aria-label={i18n.t("common.close")}
263
+ onClick={() => {
264
+ setSearchOpen(false);
265
+ setQuery("");
266
+ void viewer.current?.clearDocumentSearch();
267
+ }}
268
+ className="grid size-7 place-items-center rounded-full hover:bg-accent"
269
+ >
270
+ <X aria-hidden="true" className="size-3.5" />
271
+ </button>
272
+ </form>
273
+ ) : (
274
+ <button
275
+ type="button"
276
+ aria-label={i18n.t("attachment.viewer.search")}
277
+ onClick={() => setSearchOpen(true)}
278
+ className="grid size-8 place-items-center rounded-full hover:bg-accent"
279
+ >
280
+ <Search aria-hidden="true" className="size-4" />
281
+ </button>
282
+ )}
283
+ {!searchOpen ? (
284
+ <div className="flex items-center rounded-full bg-muted p-0.5">
285
+ <button
286
+ type="button"
287
+ aria-label={i18n.t("attachment.viewer.zoomOut")}
288
+ disabled={state.zoom ? !state.zoom.canZoomOut : false}
289
+ onClick={() => void viewer.current?.zoomOut()}
290
+ className="grid size-7 place-items-center rounded-full hover:bg-accent disabled:opacity-40"
291
+ >
292
+ <Minus aria-hidden="true" className="size-3.5" />
293
+ </button>
294
+ <button
295
+ type="button"
296
+ aria-label={i18n.t("attachment.viewer.resetZoom")}
297
+ disabled={state.zoom ? !state.zoom.canReset : false}
298
+ onClick={() => void viewer.current?.resetZoom()}
299
+ className="min-w-12 px-1 text-xs tabular-nums disabled:opacity-40"
300
+ >
301
+ {Math.round(scale * 100)}%
302
+ </button>
303
+ <button
304
+ type="button"
305
+ aria-label={i18n.t("attachment.viewer.zoomIn")}
306
+ disabled={state.zoom ? !state.zoom.canZoomIn : false}
307
+ onClick={() => void viewer.current?.zoomIn()}
308
+ className="grid size-7 place-items-center rounded-full hover:bg-accent disabled:opacity-40"
309
+ >
310
+ <Plus aria-hidden="true" className="size-3.5" />
311
+ </button>
312
+ </div>
313
+ ) : null}
314
+ {pdf && !searchOpen ? (
315
+ <div className="hidden items-center sm:flex">
316
+ <button
317
+ type="button"
318
+ aria-label={i18n.t("attachment.viewer.rotateLeft")}
319
+ onClick={() =>
320
+ void applyViewState({ rotation: ((viewState?.rotation ?? 0) - 90 + 360) % 360 })
321
+ }
322
+ className="grid size-8 place-items-center rounded-full hover:bg-accent"
323
+ >
324
+ <RotateCcw aria-hidden="true" className="size-4" />
325
+ </button>
326
+ <button
327
+ type="button"
328
+ aria-label={i18n.t("attachment.viewer.rotateRight")}
329
+ onClick={() =>
330
+ void applyViewState({ rotation: ((viewState?.rotation ?? 0) + 90) % 360 })
331
+ }
332
+ className="grid size-8 place-items-center rounded-full hover:bg-accent"
333
+ >
334
+ <RotateCw aria-hidden="true" className="size-4" />
335
+ </button>
336
+ </div>
337
+ ) : null}
338
+ </div>
339
+ ) : null}
340
+ </div>
341
+ );
342
+ }
@@ -0,0 +1,28 @@
1
+ import { lazy, Suspense } from "react";
2
+ import type { AttachmentPreviewKind } from "@/attachment-viewer/attachment-viewer.tsx";
3
+
4
+ const previews = {
5
+ pdf: lazy(async () => ({ default: (await import("./pdf-file-preview.tsx")).PdfFilePreview })),
6
+ presentation: lazy(async () => ({
7
+ default: (await import("./presentation-file-preview.tsx")).PresentationFilePreview,
8
+ })),
9
+ spreadsheet: lazy(async () => ({
10
+ default: (await import("./spreadsheet-file-preview.tsx")).SpreadsheetFilePreview,
11
+ })),
12
+ word: lazy(async () => ({ default: (await import("./word-file-preview.tsx")).WordFilePreview })),
13
+ };
14
+
15
+ export function FilePreview({
16
+ file,
17
+ kind,
18
+ }: {
19
+ file: File;
20
+ kind: Exclude<AttachmentPreviewKind, "image" | "unsupported">;
21
+ }) {
22
+ const Preview = previews[kind];
23
+ return (
24
+ <Suspense fallback={null}>
25
+ <Preview file={file} />
26
+ </Suspense>
27
+ );
28
+ }
@@ -0,0 +1,5 @@
1
+ import { pdfRenderer } from "@file-viewer/renderer-pdf";
2
+ import { FilePreviewView } from "./file-preview-view.tsx";
3
+ export function PdfFilePreview({ file }: { file: File }) {
4
+ return <FilePreviewView file={file} renderer={pdfRenderer} pdf />;
5
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ presentationRendererDefinition,
3
+ renderFileViewerPresentation,
4
+ } from "@file-viewer/renderer-presentation";
5
+ import { FilePreviewView } from "./file-preview-view.tsx";
6
+
7
+ const presentationRenderer = {
8
+ id: "intel-file-viewer-renderer-presentation",
9
+ label: "Intel PPTX renderer",
10
+ definitions: [presentationRendererDefinition],
11
+ handlers: [
12
+ {
13
+ rendererId: presentationRendererDefinition.id,
14
+ handler: renderFileViewerPresentation,
15
+ },
16
+ ],
17
+ };
18
+
19
+ export function PresentationFilePreview({ file }: { file: File }) {
20
+ return <FilePreviewView file={file} renderer={presentationRenderer} />;
21
+ }
@@ -0,0 +1,5 @@
1
+ import { spreadsheetRenderer } from "@file-viewer/renderer-spreadsheet";
2
+ import { FilePreviewView } from "./file-preview-view.tsx";
3
+ export function SpreadsheetFilePreview({ file }: { file: File }) {
4
+ return <FilePreviewView file={file} renderer={spreadsheetRenderer} />;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { wordRenderer } from "@file-viewer/renderer-word";
2
+ import { FilePreviewView } from "./file-preview-view.tsx";
3
+ export function WordFilePreview({ file }: { file: File }) {
4
+ return <FilePreviewView file={file} renderer={wordRenderer} />;
5
+ }
package/src/i18n/de.json CHANGED
@@ -31,11 +31,20 @@
31
31
  "archive.title": "Archiv",
32
32
  "auth.signOut": "Abmelden",
33
33
  "auth.signOutFailed": "Das Abmelden ist fehlgeschlagen. Prüfe deine Verbindung und versuche es erneut.",
34
+ "board.add": "Etwas hinzufügen",
35
+ "board.add.due": "Fällig",
36
+ "board.add.label": "Label",
37
+ "board.add.start": "Start",
34
38
  "board.addCard": "+ Aufgabe",
35
39
  "board.addCardIn": "Eine Aufgabe zu {column} hinzufügen",
36
40
  "board.addLabel": "Label hinzufügen",
37
41
  "board.addLink": "Verlinkung",
42
+ "board.backTo": "Zurück zu {board}",
38
43
  "board.cardLabel": "{title} öffnen, in {column}. Alt und eine Pfeiltaste verschiebt sie.",
44
+ "board.clearAssignee": "Zuständigkeit von {name} entfernen",
45
+ "board.clearDependsOn": "nicht mehr darauf warten",
46
+ "board.clearDue": "entfernen",
47
+ "board.clearStart": "entfernen",
39
48
  "board.column.archive": "Archiv",
40
49
  "board.column.assignee": "Zuständig",
41
50
  "board.column.default.backlog": "Backlog",
@@ -45,10 +54,12 @@
45
54
  "board.column.dependsOn": "Wartet auf",
46
55
  "board.column.due": "Fällig",
47
56
  "board.column.labels": "Labels",
57
+ "board.column.start": "Start",
48
58
  "board.column.status": "Spalte",
49
59
  "board.column.title": "Titel",
50
- "board.columnEmpty": "Hier ist noch nichts.",
51
60
  "board.createFailed": "Die Aufgabe konnte nicht angelegt werden.",
61
+ "board.crumbs": "Wo diese Karte sitzt",
62
+ "board.crumbsFolded": "Die Ebenen dazwischen zeigen",
52
63
  "board.drag.lifted": "{card} aufgenommen",
53
64
  "board.drag.moved": "{card} nach {column} verschoben",
54
65
  "board.drag.putBack": "{card} zurückgelegt",
@@ -65,9 +76,9 @@
65
76
  "board.link.outside": "Außerhalb dieses Boards",
66
77
  "board.link.subtask": "Unteraufgabe",
67
78
  "board.links": "Verlinkt",
68
- "board.moveTo": "In eine Spalte verschieben",
79
+ "board.moveTo": "in eine andere Spalte verschieben",
69
80
  "board.progress": "{done} von {total} erledigt",
70
- "board.removeLabel": "Label {label} entfernen",
81
+ "board.removeLabel": "dieses Label entfernen",
71
82
  "board.settings.addColumn": "+ Spalte",
72
83
  "board.settings.columnName": "Name der Spalte {column}",
73
84
  "board.settings.description": "Spalten und was das Board zeigt.",
@@ -82,6 +93,10 @@
82
93
  "board.settings.showArchive": "Archiv-Spalte zeigen",
83
94
  "board.settings.showArchiveHint": "Sie ist auf jedem Board. Ausgeblendet sind ihre Karten überall aus dem Blick.",
84
95
  "board.settings.title": "Board-Einstellungen",
96
+ "board.sort.asc": "{column}, A bis Z",
97
+ "board.sort.desc": "{column}, Z bis A",
98
+ "board.sort.none": "Unsortiert",
99
+ "board.sortBy": "Sortieren",
85
100
  "board.stripes.root": "Ebene {level}, hat Unteraufgaben",
86
101
  "board.stripes.under": "Ebene {level} · übergeordnet in {column}",
87
102
  "board.table.collapse": "Verbergen, was unter {row} liegt",
@@ -213,14 +228,55 @@
213
228
  "flows.validate": "Prüfen, ob er laufen würde",
214
229
  "flows.validateReady": "Dieser Flow würde jetzt starten.",
215
230
  "flows.validateWhen": "Geprüft {when}. Der Werkzeug-Zugriff wird jedes Mal mit deinem eigenen Token erfragt, diese Antwort ist also eine Momentaufnahme.",
231
+ "attachment.details.checksum": "Prüfsumme",
232
+ "attachment.details.dimensions": "Abmessungen",
233
+ "attachment.details.format": "Format",
234
+ "attachment.details.mimeType": "MIME-Typ",
235
+ "attachment.details.name": "Name",
236
+ "attachment.details.pages": "Seiten",
237
+ "attachment.details.private": "Privat",
238
+ "attachment.details.size": "Größe",
239
+ "attachment.details.sections": "Abschnitte",
240
+ "attachment.details.slides": "Folien",
241
+ "attachment.details.storage": "Speicher",
242
+ "attachment.details.technical": "Technisch",
243
+ "attachment.details.title": "Dateidetails",
244
+ "attachment.details.type": "Typ",
245
+ "attachment.details.uploaded": "Hochgeladen",
246
+ "attachment.details.uploadedBy": "Hochgeladen von",
247
+ "attachment.details.version": "Version",
248
+ "attachment.details.worksheets": "Tabellenblätter",
249
+ "attachment.kind.image": "Bild",
250
+ "attachment.kind.pdf": "PDF-Dokument",
251
+ "attachment.kind.presentation": "Präsentation",
252
+ "attachment.kind.spreadsheet": "Tabelle",
253
+ "attachment.kind.unsupported": "Datei",
254
+ "attachment.kind.word": "Word-Dokument",
255
+ "attachment.detailsHide": "Dateidetails ausblenden",
256
+ "attachment.detailsShow": "Dateidetails anzeigen",
257
+ "attachment.empty": "Es wurde noch keine Datei hochgeladen.",
258
+ "attachment.loadFailed": "Die Datei konnte nicht geladen werden. Das Original lässt sich weiterhin über das Menü exportieren.",
259
+ "attachment.unsupported": "Dieser Dateityp kann nicht angezeigt werden. Das Original lässt sich weiterhin über das Menü exportieren.",
260
+ "attachment.viewer.controls": "Dateisteuerung",
261
+ "attachment.viewer.navigation": "Seitennavigation anzeigen",
262
+ "attachment.viewer.nextPage": "Nächste Seite",
263
+ "attachment.viewer.nextResult": "Nächster Suchtreffer",
264
+ "attachment.viewer.previousPage": "Vorherige Seite",
265
+ "attachment.viewer.previousResult": "Vorheriger Suchtreffer",
266
+ "attachment.viewer.resetZoom": "Zoom zurücksetzen",
267
+ "attachment.viewer.rotateLeft": "Nach links drehen",
268
+ "attachment.viewer.rotateRight": "Nach rechts drehen",
269
+ "attachment.viewer.search": "Dokument durchsuchen",
270
+ "attachment.viewer.searchNoResults": "Keine Treffer",
271
+ "attachment.viewer.searchPlaceholder": "Dokument durchsuchen",
272
+ "attachment.viewer.searchResults": "Suchtreffer {current} von {total}",
273
+ "attachment.viewer.zoomIn": "Vergrößern",
274
+ "attachment.viewer.zoomOut": "Verkleinern",
216
275
  "nav.primary": "Hauptnavigation",
217
276
  "nav.tools": "Werkzeuge",
218
277
  "node.access": "Zugriff",
219
278
  "node.accessDetails": "Zugriffsdetails anzeigen ({count})",
220
- "node.attachmentHelp": "Die kanonische Datei liegt privat in Intel. Ihre KI-lesbare Projektion wird getrennt indexiert.",
221
279
  "node.changed": "Geändert",
222
- "node.download": "Datei herunterladen",
223
- "node.downloadFailed": "Der Anhang konnte nicht heruntergeladen werden. Prüfe deinen Zugriff und versuche es erneut.",
224
280
  "node.email": "E-Mail-Adresse",
225
281
  "node.empty": "Es wurde noch nichts hinzugefügt.",
226
282
  "node.folderEmpty": "In diesem Ordner ist noch nichts. Lege etwas über das Plus in der Seitenleiste an.",
package/src/i18n/en.json CHANGED
@@ -31,11 +31,20 @@
31
31
  "archive.title": "Archive",
32
32
  "auth.signOut": "Sign out",
33
33
  "auth.signOutFailed": "Signing out failed. Check your connection and try again.",
34
+ "board.add": "Add something",
35
+ "board.add.due": "Due",
36
+ "board.add.label": "Label",
37
+ "board.add.start": "Start",
34
38
  "board.addCard": "+ Task",
35
39
  "board.addCardIn": "Add a task to {column}",
36
40
  "board.addLabel": "Add a label",
37
41
  "board.addLink": "Link",
42
+ "board.backTo": "Back to {board}",
38
43
  "board.cardLabel": "Open {title}, in {column}. Alt and an arrow key moves it.",
44
+ "board.clearAssignee": "Take the assignment off {name}",
45
+ "board.clearDependsOn": "stop waiting for it",
46
+ "board.clearDue": "clear it",
47
+ "board.clearStart": "clear it",
39
48
  "board.column.archive": "Archive",
40
49
  "board.column.assignee": "Assigned to",
41
50
  "board.column.default.backlog": "Backlog",
@@ -45,10 +54,12 @@
45
54
  "board.column.dependsOn": "Waiting for",
46
55
  "board.column.due": "Due",
47
56
  "board.column.labels": "Labels",
57
+ "board.column.start": "Start",
48
58
  "board.column.status": "Column",
49
59
  "board.column.title": "Title",
50
- "board.columnEmpty": "Nothing here yet.",
51
60
  "board.createFailed": "The task could not be created.",
61
+ "board.crumbs": "Where this card sits",
62
+ "board.crumbsFolded": "Show the levels in between",
52
63
  "board.drag.lifted": "{card} picked up",
53
64
  "board.drag.moved": "{card} moved to {column}",
54
65
  "board.drag.putBack": "{card} put back",
@@ -65,9 +76,9 @@
65
76
  "board.link.outside": "Outside this board",
66
77
  "board.link.subtask": "Subtask",
67
78
  "board.links": "Linked",
68
- "board.moveTo": "Move to a column",
79
+ "board.moveTo": "move it to another column",
69
80
  "board.progress": "{done} of {total} done",
70
- "board.removeLabel": "Remove the label {label}",
81
+ "board.removeLabel": "remove this label",
71
82
  "board.settings.addColumn": "+ Column",
72
83
  "board.settings.columnName": "Name of the column {column}",
73
84
  "board.settings.description": "Columns and what the board shows.",
@@ -82,6 +93,10 @@
82
93
  "board.settings.showArchive": "Show the archive column",
83
94
  "board.settings.showArchiveHint": "It is on every board. Hidden, its cards are out of sight everywhere.",
84
95
  "board.settings.title": "Board settings",
96
+ "board.sort.asc": "{column}, A to Z",
97
+ "board.sort.desc": "{column}, Z to A",
98
+ "board.sort.none": "Unsorted",
99
+ "board.sortBy": "Sort",
85
100
  "board.stripes.root": "Level {level}, has subtasks",
86
101
  "board.stripes.under": "Level {level} · parent in {column}",
87
102
  "board.table.collapse": "Hide what is under {row}",
@@ -213,14 +228,55 @@
213
228
  "flows.validate": "Check whether it would run",
214
229
  "flows.validateReady": "This flow would start now.",
215
230
  "flows.validateWhen": "Checked {when}. Tool access is asked with your own token each time, so this answer is a snapshot.",
231
+ "attachment.details.checksum": "Checksum",
232
+ "attachment.details.dimensions": "Dimensions",
233
+ "attachment.details.format": "Format",
234
+ "attachment.details.mimeType": "MIME type",
235
+ "attachment.details.name": "Name",
236
+ "attachment.details.pages": "Pages",
237
+ "attachment.details.private": "Private",
238
+ "attachment.details.size": "Size",
239
+ "attachment.details.sections": "Sections",
240
+ "attachment.details.slides": "Slides",
241
+ "attachment.details.storage": "Storage",
242
+ "attachment.details.technical": "Technical",
243
+ "attachment.details.title": "File details",
244
+ "attachment.details.type": "Type",
245
+ "attachment.details.uploaded": "Uploaded",
246
+ "attachment.details.uploadedBy": "Uploaded by",
247
+ "attachment.details.version": "Version",
248
+ "attachment.details.worksheets": "Worksheets",
249
+ "attachment.kind.image": "Image",
250
+ "attachment.kind.pdf": "PDF document",
251
+ "attachment.kind.presentation": "Presentation",
252
+ "attachment.kind.spreadsheet": "Spreadsheet",
253
+ "attachment.kind.unsupported": "File",
254
+ "attachment.kind.word": "Word document",
255
+ "attachment.detailsHide": "Hide file details",
256
+ "attachment.detailsShow": "Show file details",
257
+ "attachment.empty": "No file has been uploaded yet.",
258
+ "attachment.loadFailed": "The file could not be loaded. You can still export the original from the menu.",
259
+ "attachment.unsupported": "This file type cannot be previewed. You can still export the original from the menu.",
260
+ "attachment.viewer.controls": "File controls",
261
+ "attachment.viewer.navigation": "Show page navigation",
262
+ "attachment.viewer.nextPage": "Next page",
263
+ "attachment.viewer.nextResult": "Next search result",
264
+ "attachment.viewer.previousPage": "Previous page",
265
+ "attachment.viewer.previousResult": "Previous search result",
266
+ "attachment.viewer.resetZoom": "Reset zoom",
267
+ "attachment.viewer.rotateLeft": "Rotate left",
268
+ "attachment.viewer.rotateRight": "Rotate right",
269
+ "attachment.viewer.search": "Search document",
270
+ "attachment.viewer.searchNoResults": "No results",
271
+ "attachment.viewer.searchPlaceholder": "Search document",
272
+ "attachment.viewer.searchResults": "Search result {current} of {total}",
273
+ "attachment.viewer.zoomIn": "Zoom in",
274
+ "attachment.viewer.zoomOut": "Zoom out",
216
275
  "nav.primary": "Primary navigation",
217
276
  "nav.tools": "Tools",
218
277
  "node.access": "Access",
219
278
  "node.accessDetails": "Show access details ({count})",
220
- "node.attachmentHelp": "The canonical file is stored privately in Intel. Its AI-readable projection is indexed separately.",
221
279
  "node.changed": "Changed",
222
- "node.download": "Download file",
223
- "node.downloadFailed": "The attachment could not be downloaded. Check your access and try again.",
224
280
  "node.email": "Email address",
225
281
  "node.empty": "Nothing has been added yet.",
226
282
  "node.folderEmpty": "Nothing in this folder yet. Create something with the plus in the sidebar.",