small changes

This commit is contained in:
2026-09-07 22:41:33 +02:00
parent 32c75f8ff1
commit d351b8e3c8
7 changed files with 250 additions and 11 deletions
+24 -1
View File
@@ -5,6 +5,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require db (require db
net/uri-codec
racket/string) racket/string)
(provide replace-current-attachment-references! (provide replace-current-attachment-references!
@@ -27,6 +28,12 @@ SQL
(define (attachment-url reference stored-name) (define (attachment-url reference stored-name)
(format "/uploads/~a/~a" reference stored-name)) (format "/uploads/~a/~a" reference stored-name))
(define (attachment-url-variants reference stored-name)
(list (attachment-url reference stored-name)
(format "/uploads/~a/~a"
reference
(uri-path-segment-unreserved-encode stored-name))))
(define (page-references db page-id) (define (page-references db page-id)
(let* ((current (let* ((current
(query-row db (query-row db
@@ -57,7 +64,8 @@ SQL
(stored-name (vector-ref row 2)) (stored-name (vector-ref row 2))
(found? #f)) (found? #f))
(for ((reference (in-list (page-references db owner-page-id)))) (for ((reference (in-list (page-references db owner-page-id))))
(when (string-contains? markdown (attachment-url reference stored-name)) (when (for/or ((url (in-list (attachment-url-variants reference stored-name))))
(string-contains? markdown url))
(set! found? #t))) (set! found? #t)))
(when found? (when found?
(query-exec db (query-exec db
@@ -89,6 +97,21 @@ SQL
(record-references! db page-id sql-null markdown #t referenced-at) (record-references! db page-id sql-null markdown #t referenced-at)
(void)) (void))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Tests for module attachment-references.rkt.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(require rackunit)
(define urls
(attachment-url-variants
"review-zuidas-2026:appreciatie"
"1788796826-35348-Review ZuidasDok.docx"))
(check-equal?
urls
(list "/uploads/review-zuidas-2026:appreciatie/1788796826-35348-Review ZuidasDok.docx"
"/uploads/review-zuidas-2026:appreciatie/1788796826-35348-Review%20ZuidasDok.docx")))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Record attachment references present in an immutable page version. ; goal : Record attachment references present in an immutable page version.
; pre : page-version-id identifies the stored version represented by markdown. ; pre : page-version-id identifies the stored version represented by markdown.
+72
View File
@@ -122,6 +122,10 @@ button { cursor: pointer; }
text-decoration: line-through; text-decoration: line-through;
} }
.EasyMDEContainer .editor-toolbar button.rw-mde-save {
display: none;
}
.EasyMDEContainer .CodeMirror, .EasyMDEContainer .CodeMirror,
.EasyMDEContainer .CodeMirror-scroll, .EasyMDEContainer .CodeMirror-scroll,
.EasyMDEContainer .editor-preview-side { .EasyMDEContainer .editor-preview-side {
@@ -222,6 +226,62 @@ body {
padding: 18px 16px 28px; padding: 18px 16px 28px;
} }
.sidebar-toggle {
display: grid;
width: 34px;
height: 34px;
margin: -8px -8px 8px auto;
place-items: center;
border: 1px solid transparent;
border-radius: 3px;
background: transparent;
color: var(--wiki-link);
}
.sidebar-toggle:hover,
.sidebar-toggle:focus-visible {
background: #fff;
border-color: var(--wiki-border);
outline: 0;
}
.sidebar-toggle svg {
width: 18px;
height: 18px;
stroke-width: 1.8;
}
body.sidebar-collapsed #app {
grid-template-columns: 52px minmax(0, 1fr);
}
body.sidebar-collapsed #sidebar {
padding: 12px 8px;
overflow: hidden;
}
body.sidebar-collapsed #sidebar > :not(#sidebar-toggle):not(.sidebar-icon-nav) {
display: none;
}
body.sidebar-collapsed #sidebar .sidebar-toggle {
margin: 0 auto;
}
body.sidebar-collapsed #sidebar .sidebar-icon-nav {
flex-direction: column;
margin: 14px 0 0;
}
body.sidebar-collapsed #sidebar .sidebar-icon-link {
width: 36px;
}
body.sidebar-collapsed #sidebar .sidebar-icon-link + .sidebar-icon-link {
border-top: 1px solid #c8cbd0;
border-left: 0;
}
.brand { .brand {
display: block; display: block;
color: #111; color: #111;
@@ -909,6 +969,10 @@ body.editor-mode .EasyMDEContainer .editor-toolbar.fullscreen {
background: white; background: white;
} }
body.editor-mode .EasyMDEContainer .editor-toolbar.fullscreen button.rw-mde-save {
display: inline-block;
}
body.editor-mode .EasyMDEContainer .editor-statusbar.fullscreen { body.editor-mode .EasyMDEContainer .editor-statusbar.fullscreen {
bottom: 0; bottom: 0;
min-height: 28px; min-height: 28px;
@@ -972,6 +1036,14 @@ body.editor-mode .EasyMDEContainer .editor-preview-side {
border-top: 0; border-top: 0;
} }
body.editor-mode .editor-shell .EasyMDEContainer:not(:has(.editor-preview-active-side)) {
grid-template-columns: minmax(0, 1fr);
}
body.editor-mode .editor-shell .EasyMDEContainer:not(:has(.editor-preview-active-side)) .CodeMirror {
grid-column: 1;
}
body.editor-mode .EasyMDEContainer .CodeMirror-sided { body.editor-mode .EasyMDEContainer .CodeMirror-sided {
width: 100% !important; width: 100% !important;
} }
+3
View File
@@ -14,6 +14,9 @@
<div id="connection-status" class="connection-status hidden" role="status"></div> <div id="connection-status" class="connection-status hidden" role="status"></div>
<div id="app"> <div id="app">
<aside id="sidebar"> <aside id="sidebar">
<button id="sidebar-toggle" class="sidebar-toggle" type="button" title="Collapse sidebar" data-tr-title="collapse-sidebar" aria-label="Collapse sidebar" data-tr-aria-label="collapse-sidebar" aria-controls="sidebar" aria-expanded="true">
<i data-lucide="panel-left-close" aria-hidden="true"></i>
</button>
<a id="wiki-brand" class="brand" href="#">Racket Wiki</a> <a id="wiki-brand" class="brand" href="#">Racket Wiki</a>
<nav class="sidebar-icon-nav" aria-label="Wiki navigation"> <nav class="sidebar-icon-nav" aria-label="Wiki navigation">
<a id="bookmarks-link" class="sidebar-icon-link" href="#bookmarks" title="Bookmarks" data-tr-title="bookmarks" aria-label="Bookmarks" data-tr-aria-label="bookmarks"><i data-lucide="star"></i><span class="visually-hidden" data-tr="bookmarks">Bookmarks</span></a> <a id="bookmarks-link" class="sidebar-icon-link" href="#bookmarks" title="Bookmarks" data-tr-title="bookmarks" aria-label="Bookmarks" data-tr-aria-label="bookmarks"><i data-lucide="star"></i><span class="visually-hidden" data-tr="bookmarks">Bookmarks</span></a>
+102 -8
View File
@@ -13,6 +13,7 @@ import {
expandNamespacedMarkdownLinks, expandNamespacedMarkdownLinks,
expandTodoMarkup, expandTodoMarkup,
expandWikiMentions, expandWikiMentions,
normalizeMarkdownLinkDestinations,
protectCamelCaseWikiWords, protectCamelCaseWikiWords,
extractCmapEmbeds, extractCmapEmbeds,
restoreCmapEmbeds restoreCmapEmbeds
@@ -70,6 +71,8 @@ import { ComboBox } from "./widgets/combobox.js";
let easyMDE = null; let easyMDE = null;
let pendingWikiCmapLinkLabel = ""; let pendingWikiCmapLinkLabel = "";
const sidebarPreferenceKey = "racket-wiki-sidebar-collapsed";
const editorSideBySidePreferenceKey = "racket-wiki-editor-side-by-side";
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// General UI and HTTP support // General UI and HTTP support
@@ -133,6 +136,50 @@ import { ComboBox } from "./widgets/combobox.js";
document.documentElement.lang = state.language || "en"; document.documentElement.lang = state.language || "en";
} }
function setSidebarCollapsed(collapsed, remember = true) {
document.body.classList.toggle("sidebar-collapsed", collapsed);
const toggle = $("sidebar-toggle");
if (!toggle) return;
const translationKey = collapsed ? "expand-sidebar" : "collapse-sidebar";
toggle.dataset.trTitle = translationKey;
toggle.dataset.trAriaLabel = translationKey;
toggle.title = tr(translationKey, collapsed ? "Expand sidebar" : "Collapse sidebar");
toggle.setAttribute("aria-label", toggle.title);
toggle.setAttribute("aria-expanded", String(!collapsed));
const icon = document.createElement("i");
icon.dataset.lucide = collapsed ? "panel-left-open" : "panel-left-close";
icon.setAttribute("aria-hidden", "true");
const oldIcon = toggle.querySelector("[data-lucide], svg");
if (oldIcon) {
oldIcon.replaceWith(icon);
} else {
toggle.append(icon);
}
if (window.lucide && typeof window.lucide.createIcons === "function") {
window.lucide.createIcons();
}
if (remember) {
try {
window.sessionStorage.setItem(sidebarPreferenceKey, collapsed ? "true" : "false");
} catch (_error) {
// A blocked session storage should not prevent the toggle from working.
}
}
}
function initializeSidebarToggle() {
const toggle = $("sidebar-toggle");
if (!toggle) return;
let collapsed = false;
try {
collapsed = window.sessionStorage.getItem(sidebarPreferenceKey) === "true";
} catch (_error) {
collapsed = false;
}
setSidebarCollapsed(collapsed, false);
toggle.addEventListener("click", () => setSidebarCollapsed(!document.body.classList.contains("sidebar-collapsed")));
}
function navigateToHash(targetHash) { function navigateToHash(targetHash) {
const navigate = () => { const navigate = () => {
if (location.hash === targetHash) return route(); if (location.hash === targetHash) return route();
@@ -204,7 +251,8 @@ import { ComboBox } from "./widgets/combobox.js";
function renderMarkdown(markdown, pageSlug = null) { function renderMarkdown(markdown, pageSlug = null) {
const extracted = extractCmapEmbeds(markdown || ""); const extracted = extractCmapEmbeds(markdown || "");
const withExplicitWikiLinks = expandNamespacedMarkdownLinks(extracted.markdown); const normalizedLinks = normalizeMarkdownLinkDestinations(extracted.markdown);
const withExplicitWikiLinks = expandNamespacedMarkdownLinks(normalizedLinks);
const withWikiLinks = expandWikiMentions( const withWikiLinks = expandWikiMentions(
withExplicitWikiLinks, withExplicitWikiLinks,
state.pages, state.pages,
@@ -436,6 +484,32 @@ import { ComboBox } from "./widgets/combobox.js";
easyMDE.codemirror.refresh(); easyMDE.codemirror.refresh();
} }
function preferredEditorSideBySide() {
try {
const stored = window.sessionStorage.getItem(editorSideBySidePreferenceKey);
return stored === null ? true : stored === "true";
} catch (_error) {
return true;
}
}
function rememberEditorSideBySide() {
if (!easyMDE) return;
try {
window.sessionStorage.setItem(
editorSideBySidePreferenceKey,
String(easyMDE.isSideBySideActive())
);
} catch (_error) {
// A blocked session storage should not prevent the editor from working.
}
}
function toggleEditorSideBySide() {
easyMDE.toggleSideBySide();
rememberEditorSideBySide();
}
function protectCamelCaseInPage() { function protectCamelCaseInPage() {
if (!easyMDE) return; if (!easyMDE) return;
const source = easyMDE.value(); const source = easyMDE.value();
@@ -523,9 +597,10 @@ import { ComboBox } from "./widgets/combobox.js";
"|", "|",
toolbarButton("undo", EasyMDE.undo, "undo-2", tr("undo", "Undo")), toolbarButton("undo", EasyMDE.undo, "undo-2", tr("undo", "Undo")),
toolbarButton("redo", EasyMDE.redo, "redo-2", tr("redo", "Redo")), toolbarButton("redo", EasyMDE.redo, "redo-2", tr("redo", "Redo")),
toolbarButton("save", savePageFromFullscreen, "save", tr("save", "Save"), { noDisable: true }),
toolbarButton("raw-markdown", toggleRawMarkdown, "file-text", tr("raw-markdown", "Raw Markdown"), { noDisable: true }), toolbarButton("raw-markdown", toggleRawMarkdown, "file-text", tr("raw-markdown", "Raw Markdown"), { noDisable: true }),
toolbarButton("preview", EasyMDE.togglePreview, "eye", tr("preview", "Preview"), { noDisable: true }), toolbarButton("preview", EasyMDE.togglePreview, "eye", tr("preview", "Preview"), { noDisable: true }),
toolbarButton("side-by-side", EasyMDE.toggleSideBySide, "columns-2", tr("side-by-side", "Side by side"), { noDisable: true, noMobile: true }), toolbarButton("side-by-side", toggleEditorSideBySide, "columns-2", tr("side-by-side", "Side by side"), { noDisable: true, noMobile: true }),
toolbarButton("fullscreen", EasyMDE.toggleFullScreen, "maximize", tr("fullscreen", "Fullscreen"), { noDisable: true, noMobile: true }) toolbarButton("fullscreen", EasyMDE.toggleFullScreen, "maximize", tr("fullscreen", "Fullscreen"), { noDisable: true, noMobile: true })
] ]
}); });
@@ -575,7 +650,7 @@ import { ComboBox } from "./widgets/combobox.js";
requestAnimationFrame(() => { requestAnimationFrame(() => {
easyMDE.codemirror.refresh(); easyMDE.codemirror.refresh();
const wideScreen = window.matchMedia("(min-width: 901px)").matches; const wideScreen = window.matchMedia("(min-width: 901px)").matches;
if (wideScreen && !easyMDE.isSideBySideActive()) { if (wideScreen && easyMDE.isSideBySideActive() !== preferredEditorSideBySide()) {
easyMDE.toggleSideBySide(); easyMDE.toggleSideBySide();
} }
updateEditorChromeMetrics(); updateEditorChromeMetrics();
@@ -1045,6 +1120,17 @@ import { ComboBox } from "./widgets/combobox.js";
* post : A successful save refreshes page metadata and opens the stored page. * post : A successful save refreshes page metadata and opens the stored page.
*/ */
async function savePage() { async function savePage() {
return savePageWithOptions();
}
function savePageFromFullscreen() {
savePageWithOptions({ keepEditing: true }).catch((error) => {
$("save-status").textContent = error.message;
});
}
/** Save the current Markdown and optionally keep the editor active. */
async function savePageWithOptions({ keepEditing = false } = {}) {
const title = $("editor-title").value.trim(); const title = $("editor-title").value.trim();
const namespace = $("editor-namespace").value.trim(); const namespace = $("editor-namespace").value.trim();
const currentSlug = state.editingNew ? state.newPageSlug : state.currentPage?.slug; const currentSlug = state.editingNew ? state.newPageSlug : state.currentPage?.slug;
@@ -1096,8 +1182,14 @@ import { ComboBox } from "./widgets/combobox.js";
applyTranslations(); applyTranslations();
$("account-role").textContent = tr(`role-${state.session.user.role}`, state.session.user.role); $("account-role").textContent = tr(`role-${state.session.user.role}`, state.session.user.role);
} }
location.hash = `#/${encodeURIComponent(page.slug)}`; if (keepEditing) {
await openPage(page.slug); $("editor-namespace").disabled = true;
updateEditorSlugInfo();
$("edit-summary").value = "";
} else {
location.hash = `#/${encodeURIComponent(page.slug)}`;
await openPage(page.slug);
}
$("save-status").textContent = tr("saved", "Saved"); $("save-status").textContent = tr("saved", "Saved");
} catch (error) { } catch (error) {
$("save-status").textContent = error.message; $("save-status").textContent = error.message;
@@ -1189,7 +1281,7 @@ import { ComboBox } from "./widgets/combobox.js";
} }
const result = await uploadOneFile(file); const result = await uploadOneFile(file);
$("save-status").textContent = tr("image-upload-complete", "Image upload complete"); $("save-status").textContent = tr("image-upload-complete", "Image upload complete");
return result.url; return encodeURI(result.url);
} }
/** /**
@@ -1206,9 +1298,10 @@ import { ComboBox } from "./widgets/combobox.js";
for (const file of fileList) { for (const file of fileList) {
const result = await uploadOneFile(file); const result = await uploadOneFile(file);
const escapedName = file.name.replace(/[\[\]]/g, "\\$&"); const escapedName = file.name.replace(/[\[\]]/g, "\\$&");
const url = encodeURI(result.url);
const markdown = isInlineImage(file) const markdown = isInlineImage(file)
? `![${escapedName}](${result.url})` ? `![${escapedName}](${url})`
: `[${escapedName}](${result.url})`; : `[${escapedName}](${url})`;
insertTextAtCursor(`\n${markdown}\n`); insertTextAtCursor(`\n${markdown}\n`);
} }
$("save-status").textContent = tr("upload-complete", "Upload complete"); $("save-status").textContent = tr("upload-complete", "Upload complete");
@@ -2023,6 +2116,7 @@ import { ComboBox } from "./widgets/combobox.js";
state.translationTemplate = translationData.template || ""; state.translationTemplate = translationData.template || "";
state.translations = translationData.translations || {}; state.translations = translationData.translations || {};
applyTranslations(); applyTranslations();
initializeSidebarToggle();
await cmapWorkspace.initialize(); await cmapWorkspace.initialize();
initializeEditor(); initializeEditor();
$("account-name").textContent = state.session.user.displayName; $("account-name").textContent = state.session.user.displayName;
+39
View File
@@ -182,6 +182,44 @@ function positionIsProtected(start, end, ranges) {
return false; return false;
} }
/** Normalize link destinations that contain whitespace before Marked parses them. */
function normalizeMarkdownLinkDestinations(markdown) {
const lines = String(markdown || "").split("\n");
const result = [];
let fence = null;
const linkPattern = /(!?\[[^\]\n]*\]\()([^\)\n]*)(\))/g;
for (const line of lines) {
const fenceMatch = line.match(/^\s*(```+|~~~+)/);
if (fenceMatch) {
const marker = fenceMatch[1].charAt(0);
fence = fence === null ? marker : (fence === marker ? null : fence);
result.push(line);
continue;
}
if (fence !== null || /^\s{4}/.test(line)) {
result.push(line);
continue;
}
const codeRanges = [];
for (const match of line.matchAll(/`+[^`\n]*`+/g)) {
codeRanges.push({ start: match.index, end: match.index + match[0].length });
}
result.push(line.replace(linkPattern, (match, before, body, after, offset) => {
if (positionIsProtected(offset, offset + match.length, codeRanges)) return match;
const trimmed = body.trim();
if (!trimmed || trimmed.startsWith("<") || !/\s/.test(trimmed)) return match;
const titleMatch = trimmed.match(/^(.+?)(\s+(?:"[^"\n]*"|'[^'\n]*'|\([^\)\n]*\)))$/);
const destination = titleMatch ? titleMatch[1] : trimmed;
const title = titleMatch ? titleMatch[2] : "";
return `${before}${encodeURI(destination)}${title}${after}`;
}));
}
return result.join("\n");
}
/** Prefix every unprotected CamelCase WikiWord with the literal escape marker. */ /** Prefix every unprotected CamelCase WikiWord with the literal escape marker. */
function protectCamelCaseWikiWords(markdown) { function protectCamelCaseWikiWords(markdown) {
const wikiWordPattern = /(?<![!\p{L}\p{N}._-])((?:\p{Lu}\p{Ll}+){2,})(?![\p{L}\p{N}_-])/gu; const wikiWordPattern = /(?<![!\p{L}\p{N}._-])((?:\p{Lu}\p{Ll}+){2,})(?![\p{L}\p{N}_-])/gu;
@@ -363,6 +401,7 @@ export {
expandNamespacedMarkdownLinks, expandNamespacedMarkdownLinks,
expandTodoMarkup, expandTodoMarkup,
expandWikiMentions, expandWikiMentions,
normalizeMarkdownLinkDestinations,
protectCamelCaseWikiWords, protectCamelCaseWikiWords,
extractCmapEmbeds, extractCmapEmbeds,
restoreCmapEmbeds restoreCmapEmbeds
+8
View File
@@ -9,6 +9,7 @@ import {
expandTodoMarkup, expandTodoMarkup,
expandWikiMentions, expandWikiMentions,
extractCmapEmbeds, extractCmapEmbeds,
normalizeMarkdownLinkDestinations,
protectCamelCaseWikiWords, protectCamelCaseWikiWords,
restoreCmapEmbeds restoreCmapEmbeds
} from "../static/js/wiki/markdown.js"; } from "../static/js/wiki/markdown.js";
@@ -39,6 +40,13 @@ test("explicit namespaced links become internal routes outside code", () => {
"[Page](#/notes%3Aroadmap)\n[CMap](#cmap/architecture)\n```\n[Code](notes:keep)\n```"); "[Page](#/notes%3Aroadmap)\n[CMap](#cmap/architecture)\n```\n[Code](notes:keep)\n```");
}); });
test("link destinations with spaces are encoded without changing code", () => {
const markdown = "* [DOC](/uploads/review-zuidas-2026:appreciatie/1788796826-35348-Review ZuidasDok - Hans Dijkema - Beschouwing v3.docx)\n`[code](/uploads/page/file name.txt)`\n```\n[code](/uploads/page/file name.txt)\n```";
assert.equal(
normalizeMarkdownLinkDestinations(markdown),
"* [DOC](/uploads/review-zuidas-2026:appreciatie/1788796826-35348-Review%20ZuidasDok%20-%20Hans%20Dijkema%20-%20Beschouwing%20v3.docx)\n`[code](/uploads/page/file name.txt)`\n```\n[code](/uploads/page/file name.txt)\n```");
});
test("WikiWords use page and CMap catalogues without expanding protected text", () => { test("WikiWords use page and CMap catalogues without expanding protected text", () => {
const pages = [ const pages = [
{ slug: "homeopathie", namespace: "", pageSlug: "homeopathie", title: "Homeopathie Wiki" } { slug: "homeopathie", namespace: "", pageSlug: "homeopathie", title: "Homeopathie Wiki" }
+2 -2
View File
@@ -144,7 +144,7 @@
'bulleted-list "Bulleted list" 'numbered-list "Numbered list" 'checklist "Checklist" 'code-block "Code block" 'bulleted-list "Bulleted list" 'numbered-list "Numbered list" 'checklist "Checklist" 'code-block "Code block"
'table "Table" 'link "Link" 'horizontal-rule "Horizontal rule" 'undo "Undo" 'redo "Redo" 'table "Table" 'link "Link" 'horizontal-rule "Horizontal rule" 'undo "Undo" 'redo "Redo"
'preview "Preview" 'side-by-side "Side by side" 'fullscreen "Fullscreen" 'raw-markdown "Raw Markdown" 'automatic-wiki-link "Automatic wiki link" 'preview "Preview" 'side-by-side "Side by side" 'fullscreen "Fullscreen" 'raw-markdown "Raw Markdown" 'automatic-wiki-link "Automatic wiki link"
'language "Language" 'language-en "English" 'language-nl "Dutch" 'language "Language" 'language-en "English" 'language-nl "Dutch" 'collapse-sidebar "Collapse sidebar" 'expand-sidebar "Expand sidebar"
'no-headings "No headings" 'uploading "Uploading" 'image-upload-complete "Image upload complete" 'upload-complete "Upload complete" 'no-headings "No headings" 'uploading "Uploading" 'image-upload-complete "Image upload complete" 'upload-complete "Upload complete"
'by "by" 'result "result" 'results "results" 'for "for" 'page-does-not-exist "The page does not exist.")) 'by "by" 'result "result" 'results "results" 'for "for" 'page-does-not-exist "The page does not exist."))
@@ -270,7 +270,7 @@
'bulleted-list "Opsomming" 'numbered-list "Genummerde lijst" 'checklist "Checklist" 'code-block "Codeblok" 'bulleted-list "Opsomming" 'numbered-list "Genummerde lijst" 'checklist "Checklist" 'code-block "Codeblok"
'table "Tabel" 'link "Link" 'horizontal-rule "Horizontale lijn" 'undo "Ongedaan maken" 'redo "Opnieuw uitvoeren" 'table "Tabel" 'link "Link" 'horizontal-rule "Horizontale lijn" 'undo "Ongedaan maken" 'redo "Opnieuw uitvoeren"
'preview "Voorbeeld" 'side-by-side "Naast elkaar" 'fullscreen "Volledig scherm" 'raw-markdown "Ruwe Markdown" 'automatic-wiki-link "Automatische wikilink" 'preview "Voorbeeld" 'side-by-side "Naast elkaar" 'fullscreen "Volledig scherm" 'raw-markdown "Ruwe Markdown" 'automatic-wiki-link "Automatische wikilink"
'language "Taal" 'language-en "Engels" 'language-nl "Nederlands" 'language "Taal" 'language-en "Engels" 'language-nl "Nederlands" 'collapse-sidebar "Zijpaneel inklappen" 'expand-sidebar "Zijpaneel uitklappen"
'no-headings "Geen koppen" 'uploading "Uploaden" 'image-upload-complete "Afbeelding geüpload" 'upload-complete "Upload voltooid" 'no-headings "Geen koppen" 'uploading "Uploaden" 'image-upload-complete "Afbeelding geüpload" 'upload-complete "Upload voltooid"
'by "door" 'result "resultaat" 'results "resultaten" 'for "voor" 'page-does-not-exist "De pagina bestaat niet.")) 'by "door" 'result "resultaat" 'results "resultaten" 'for "voor" 'page-does-not-exist "De pagina bestaat niet."))