feat: add profile language preferences and refine toast, dropdown and history UI

This commit is contained in:
Mateusz Gruszczyński
2026-09-04 23:48:09 +02:00
parent f036240d5d
commit bf13587a71
42 changed files with 3971 additions and 357 deletions
+7 -6
View File
@@ -8,6 +8,7 @@
*/
import { EMOJI_SHORTCODES } from "@rustpad/emoji-data";
import { t } from "@rustpad/i18n";
import { parseImageAlias } from "@rustpad/image-alias";
function escapeHtml(value) {
@@ -224,7 +225,7 @@ function renderStandaloneMedia(line, sourceLine) {
const label = String(videoAlias[2] || filename).trim() || filename;
const playbackUrl = safeAttachmentPlaybackUrl(file);
const downloadUrl = safeAttachmentDownloadUrl(file);
return `<div class="rustpad-media preview-source-line" data-source-line="${sourceLine}" contenteditable="false"><video class="rustpad-media__player" data-rustpad-player data-player-kind="video" controls playsinline preload="metadata" aria-label="${escapeHtml(label)}"><source src="${playbackUrl}" type="${escapeHtml(file.mimeType)}"></video><p class="rustpad-media__fallback" hidden>Playback is unavailable. <a href="${downloadUrl}" download="${escapeHtml(filename)}">Download ${escapeHtml(label)}</a>.</p></div>`;
return `<div class="rustpad-media preview-source-line" data-source-line="${sourceLine}" contenteditable="false"><video class="rustpad-media__player" data-rustpad-player data-player-kind="video" controls playsinline preload="metadata" aria-label="${escapeHtml(label)}"><source src="${playbackUrl}" type="${escapeHtml(file.mimeType)}"></video><p class="rustpad-media__fallback" hidden>${escapeHtml(t("markdown.playbackUnavailable", {}, "Playback is unavailable."))} <a href="${downloadUrl}" download="${escapeHtml(filename)}">${escapeHtml(t("markdown.download", { label }, `Download ${label}`))}</a>.</p></div>`;
}
const trimmed = String(line).trim();
@@ -232,8 +233,8 @@ function renderStandaloneMedia(line, sourceLine) {
const candidate = markdownLink ? markdownLink[2] : trimmed;
const youtube = youtubeVideo(candidate);
if (!youtube) return null;
const title = markdownLink?.[1]?.trim() || "YouTube video";
return `<div class="rustpad-media preview-source-line" data-source-line="${sourceLine}" contenteditable="false"><div class="rustpad-media__player" data-rustpad-player data-player-kind="youtube" data-video-id="${escapeHtml(youtube.id)}" data-player-title="${escapeHtml(title)}"><p class="rustpad-media__fallback"><a href="${safeUrl(youtube.url)}" target="_blank" rel="noopener noreferrer">Open ${escapeHtml(title)}</a></p></div></div>`;
const title = markdownLink?.[1]?.trim() || t("markdown.youtubeVideo", {}, "YouTube video");
return `<div class="rustpad-media preview-source-line" data-source-line="${sourceLine}" contenteditable="false"><div class="rustpad-media__player" data-rustpad-player data-player-kind="youtube" data-video-id="${escapeHtml(youtube.id)}" data-player-title="${escapeHtml(title)}"><p class="rustpad-media__fallback"><a href="${safeUrl(youtube.url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(t("markdown.open", { title }, `Open ${title}`))}</a></p></div></div>`;
}
function listLine(line) {
@@ -462,11 +463,11 @@ export function renderMarkdown(source, lineOffset = 0) {
while (end < lines.length && !/^<\/details>\s*$/i.test(lines[end].trim())) end++;
if (end < lines.length) {
let bodyStart = index + 1;
let summary = "Details";
let summary = t("markdown.details", {}, "Details");
while (bodyStart < end && !lines[bodyStart].trim()) bodyStart++;
if (bodyStart < end) {
const summaryMatch = lines[bodyStart].trim().match(/^<summary>([\s\S]*?)<\/summary>$/i);
if (summaryMatch) { summary = summaryMatch[1].trim() || "Details"; bodyStart++; }
if (summaryMatch) { summary = summaryMatch[1].trim() || t("markdown.details", {}, "Details"); bodyStart++; }
}
while (bodyStart < end && !lines[bodyStart].trim()) bodyStart++;
const body = lines.slice(bodyStart, end).join("\n");
@@ -480,7 +481,7 @@ export function renderMarkdown(source, lineOffset = 0) {
closeList();
const tocHeadings = headings.filter(item => item.index > index);
if (tocHeadings.length) {
html += `<nav class="markdown-toc preview-source-line" data-source-line="${index + lineOffset + 1}" aria-label="Table of contents">${renderTableOfContentsList(tableOfContentsTree(tocHeadings))}</nav>`;
html += `<nav class="markdown-toc preview-source-line" data-source-line="${index + lineOffset + 1}" aria-label="${escapeHtml(t("markdown.toc", {}, "Table of contents"))}">${renderTableOfContentsList(tableOfContentsTree(tocHeadings))}</nav>`;
}
continue;
}