Put the interface into English, and give the notebook one way in
The interface had drifted into two languages — the frame in English, everything added later in German — which is worse than either, since the same idea appeared under two names depending on when that part was written. Sorting by title still uses the German collation, so Übung 2 comes before Übung 10: that is about the notes, not the chrome. Two archive folders were renamed with it (bilder/ → images/, zeichnungen/ → drawings/). The importer reads both spellings, and both the old and new marker line for a drawing — archives exported before this are on disk, and an import that quietly dropped their drawings would be the worst kind of tidy-up. Making a note, uploading a PDF and importing Markdown were three buttons side by side, two of them icons you had to hover to identify. They are the same act — putting something new in this notebook — so they are now one + menu. The search box moves out of the sidebar and into the note list, labelled with the notebook it narrows. Nothing changed about what it does: it has always searched only the notebook you are in, but from the sidebar it read as searching everything. A separator no longer carries a sentence explaining the order the sort menu already names. Renaming joins removing on the right-click menu — a left-click on the label started an edit, so the row could not be touched without falling into one. 218 unit tests, 238 browser checks across eleven suites. Two of those caught real breakage from this change rather than needing a translation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0a7cf26b28
commit
52bd2b8f3d
27 files changed
+469
-336
No files matched your search
+20
-13
@@ -71,6 +71,9 @@ export default function App() {
|
||||
store a choice — "All Notes" is not a thing that can carry a setting — so it
|
||||
lasts for the session and no longer. */
|
||||
const [virtualSort, setVirtualSort] = useState({});
|
||||
// Which separator is being renamed. Held here because the context menu that
|
||||
// starts it lives here too.
|
||||
const [renamingSeparator, setRenamingSeparator] = useState(null);
|
||||
const [historyFor, setHistoryFor] = useState(null);
|
||||
const [ctxMenu, setCtxMenu] = useState(null);
|
||||
const [confirmDialog, setConfirmDialog] = useState(null);
|
||||
@@ -209,17 +212,19 @@ export default function App() {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
items: [
|
||||
{ label: "Rename", icon: I.edit, action: () => setRenamingSeparator(separator.id) },
|
||||
{ divider: true },
|
||||
{
|
||||
label: "Trenner entfernen",
|
||||
label: "Remove separator",
|
||||
icon: I.trash,
|
||||
danger: true,
|
||||
action: () =>
|
||||
confirm({
|
||||
title: "Trenner entfernen",
|
||||
title: "Remove separator",
|
||||
message: separator.label
|
||||
? `Trennlinie "${separator.label}" entfernen? Die Notizen bleiben.`
|
||||
: "Diese Trennlinie entfernen? Die Notizen bleiben.",
|
||||
confirmLabel: "Entfernen",
|
||||
? `Remove the separator "${separator.label}"? The notes stay.`
|
||||
: "Remove this separator? The notes stay.",
|
||||
confirmLabel: "Remove",
|
||||
onConfirm: () => removeSeparator(separator.id),
|
||||
}),
|
||||
},
|
||||
@@ -354,28 +359,28 @@ export default function App() {
|
||||
|
||||
const reportFailure = (message) =>
|
||||
setConfirmDialog({
|
||||
title: "Import fehlgeschlagen",
|
||||
title: "Import failed",
|
||||
message,
|
||||
confirmLabel: "OK",
|
||||
onConfirm: () => setConfirmDialog(null),
|
||||
});
|
||||
|
||||
const importPdf = () =>
|
||||
withNotebook("PDF importieren", (folderId) =>
|
||||
withNotebook("Upload PDF", (folderId) =>
|
||||
chooseFiles("application/pdf,.pdf", false, async ([file]) => {
|
||||
try {
|
||||
const note = await data.importPdf(folderId, file);
|
||||
if (VIRTUAL_FOLDERS.includes(activeFolder)) setActiveFolder(folderId);
|
||||
openNote(note.id);
|
||||
} catch (e) {
|
||||
reportFailure(e?.message || "Die Datei konnte nicht importiert werden.");
|
||||
reportFailure(e?.message || "The file could not be imported.");
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
/** Markdown files, or a ZIP written by this app's export. */
|
||||
const importMarkdown = () =>
|
||||
withNotebook("Notizen importieren", (folderId) =>
|
||||
withNotebook("Import notes", (folderId) =>
|
||||
chooseFiles(".md,.markdown,.txt,.zip,text/markdown,application/zip", true, (files) => {
|
||||
if (VIRTUAL_FOLDERS.includes(activeFolder)) setActiveFolder(folderId);
|
||||
setImporting({ files, folderId });
|
||||
@@ -383,7 +388,7 @@ export default function App() {
|
||||
);
|
||||
|
||||
const createNote = () =>
|
||||
withNotebook("Neue Notiz", async (folderId) => {
|
||||
withNotebook("New note", async (folderId) => {
|
||||
const note = await data.createNote(folderId);
|
||||
if (VIRTUAL_FOLDERS.includes(activeFolder)) setActiveFolder(folderId);
|
||||
openNote(note.id);
|
||||
@@ -586,8 +591,6 @@ export default function App() {
|
||||
isMobile={isMobile}
|
||||
open={sidebarOpen}
|
||||
onClose={() => setSidebarOpen(false)}
|
||||
searchQuery={searchQuery}
|
||||
onSearchChange={setSearchQuery}
|
||||
activeFolder={activeFolder}
|
||||
onSelectFolder={openFolder}
|
||||
notebooks={notebooks}
|
||||
@@ -632,12 +635,16 @@ export default function App() {
|
||||
isTrashView={isTrashView}
|
||||
trashedCount={trashedNotes.length}
|
||||
snippetFor={search.snippetFor}
|
||||
query={searchQuery}
|
||||
onQueryChange={setSearchQuery}
|
||||
sortMode={sortMode}
|
||||
sortModes={sortModesFor(!!activeNotebook)}
|
||||
onSortModeChange={setSortMode}
|
||||
reorderable={canReorder(sortMode, isSearching) && !!activeNotebook}
|
||||
separators={separators}
|
||||
onAddSeparator={addSeparator}
|
||||
renamingSeparator={renamingSeparator}
|
||||
onStartRenameSeparator={setRenamingSeparator}
|
||||
onRenameSeparator={renameSeparator}
|
||||
onSeparatorContextMenu={separatorContextMenu}
|
||||
dnd={noteDnd}
|
||||
@@ -700,7 +707,7 @@ export default function App() {
|
||||
}}
|
||||
onExportAll={() => {
|
||||
setShowAccount(false);
|
||||
setExporting({ label: "Alle Notizen", notes: activeNotes });
|
||||
setExporting({ label: "All Notes", notes: activeNotes });
|
||||
}}
|
||||
dark={dark}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user