diff --git a/Cargo.toml b/Cargo.toml
index 6c8d9cb..20a8c40 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rustpad"
-version = "0.0.13"
+version = "0.0.14"
edition = "2024"
rust-version = "1.94"
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
diff --git a/static/css/styles.css b/static/css/styles.css
index bbb1666..f71f500 100644
--- a/static/css/styles.css
+++ b/static/css/styles.css
@@ -1292,7 +1292,7 @@ dialog::backdrop {
.public-content {
min-height: 240px;
- padding: 32px;
+ padding: 32px 32px 32px 62px;
border: 1px solid var(--border);
border-radius: 14px;
background: var(--surface);
@@ -1308,7 +1308,7 @@ dialog::backdrop {
}
.public-content {
- padding: 20px;
+ padding: 20px 20px 20px 52px;
}
}
@@ -2179,13 +2179,15 @@ dialog::backdrop {
}
.preview-source-line {
+ position: relative;
min-height: 1.32em;
}
.preview-source-line::before {
content: attr(data-source-line);
position: absolute;
- left: 12px;
+ top: 0;
+ left: -50px;
width: 32px;
color: #596270;
text-align: right;
diff --git a/static/js/api.js b/static/js/api.js
index 16edb53..0980ea1 100644
--- a/static/js/api.js
+++ b/static/js/api.js
@@ -14,7 +14,7 @@ export async function api(path, options = {}) {
const contentType = response.headers.get("content-type") || "";
const data = contentType.includes("application/json") ? await response.json().catch(() => ({})) : {};
if (!response.ok) {
- const defaults = { 400: "Invalid request.", 401: "Authentication required.", 403: "Access denied.", 404: "The requested resource was not found.", 405: "This operation is not allowed.", 409: "The requested change conflicts with existing data.", 413: "The uploaded data is too large.", 429: "Too many requests. Try again later.", 500: "Server error. Try again later.", 503: "Service temporarily unavailable." };
+ const defaults = { 400: "Invalid request.", 401: "Authentication required.", 403: "Access denied.", 404: "The requested resource was not found.", 405: "This operation is not allowed.", 409: "The requested change conflicts with existing data.", 413: "The selected file exceeds the allowed upload limit.", 429: "Too many requests. Try again later.", 500: "Server error. Try again later.", 503: "Service temporarily unavailable." };
const requestError = new Error(data.error || defaults[response.status] || `Request failed (${response.status}).`);
requestError.status = response.status;
logWarn("api.failed", { method: options.method || "GET", path, status: response.status, message: requestError.message });
diff --git a/static/js/editor-format.js b/static/js/editor-format.js
index cd05c00..daa9ef4 100644
--- a/static/js/editor-format.js
+++ b/static/js/editor-format.js
@@ -50,7 +50,14 @@ export function applyFormat(editor, format) {
if (format === "subscript") toggleWrap(editor, "~", "~", "2");
if (format === "superscript") toggleWrap(editor, "^", "^", "2");
if (format === "codeblock") toggleWrap(editor, "```text\n", "\n```", "code");
+ if (format === "codeblock-lines") toggleWrap(editor, "```text=\n", "\n```", "code");
+ if (format === "mermaid") toggleWrap(editor, "```mermaid\n", "\n```", "graph TD\n A[Start] --> B[End]");
if (format === "details") toggleWrap(editor, "\nClick me
\n\n", "\n ", "Content");
+ if (format === "toc") toggleWrap(editor, "", "", "[TOC]");
+ if (format.startsWith("alert-")) {
+ const type = format.slice("alert-".length);
+ toggleWrap(editor, `:::${type}\n`, "\n:::", "Alert content");
+ }
if (format === "table") toggleWrap(editor, "| Column 1 | Column 2 |\n| --- | --- |\n| ", " | value |", "value");
if (format === "footnote") toggleWrap(editor, "", "[^1]\n\n[^1]: Footnote text", "Text with footnote");
if (format === "definition") toggleWrap(editor, "", "\n: Definition", "Term");
diff --git a/static/note.html b/static/note.html
index af866ed..d0af508 100644
--- a/static/note.html
+++ b/static/note.html
@@ -46,9 +46,16 @@
More