This commit is contained in:
Mateusz Gruszczyński
2026-07-25 18:19:26 +02:00
parent be374980d4
commit f4220a7a38
6 changed files with 34 additions and 42 deletions
Generated
+1 -1
View File
@@ -2433,7 +2433,7 @@ dependencies = [
[[package]] [[package]]
name = "rustpad" name = "rustpad"
version = "0.0.24" version = "0.0.25"
dependencies = [ dependencies = [
"argon2", "argon2",
"aws-config", "aws-config",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rustpad" name = "rustpad"
version = "0.0.24" version = "0.0.25"
edition = "2024" edition = "2024"
rust-version = "1.94" rust-version = "1.94"
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL" description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
+15 -19
View File
@@ -15,7 +15,7 @@ import { askConfirm } from "@rustpad/modal";
import { currentShareUrl, readEditorState, writeEditorState } from "@rustpad/url-state"; import { currentShareUrl, readEditorState, writeEditorState } from "@rustpad/url-state";
const parts = location.pathname.split("/").filter(Boolean), workspaceSlug = parts[1], noteSlug = parts[3]; const parts = location.pathname.split("/").filter(Boolean), workspaceSlug = parts[1], noteSlug = parts[3];
const editor = document.querySelector("#editor"), preview = document.querySelector("#preview"), editorWorkspace = document.querySelector("#editor-workspace"), gutter = document.querySelector("#line-gutter"), ownerLabels = document.querySelector("#owner-labels"), authorshipLayer = document.querySelector("#authorship-layer"), documentOwnerBadge = document.querySelector("#document-owner-badge"); const editor = document.querySelector("#editor"), preview = document.querySelector("#preview"), editorWorkspace = document.querySelector("#editor-workspace"), gutter = document.querySelector("#line-gutter"), ownerLabels = document.querySelector("#owner-labels"), authorshipLayer = document.querySelector("#authorship-layer");
const modeToggle = document.querySelector("#mode-toggle"), passwordDialog = document.querySelector("#password-dialog"), identityDialog = document.querySelector("#identity-dialog"); const modeToggle = document.querySelector("#mode-toggle"), passwordDialog = document.querySelector("#password-dialog"), identityDialog = document.querySelector("#identity-dialog");
const roomDetails = document.querySelector("#room-details"), roomUsers = document.querySelector("#room-users"), roomCount = document.querySelector("#room-count"), socketLatency = document.querySelector("#socket-latency"), chatMessages = document.querySelector("#chat-messages"), chatForm = document.querySelector("#chat-form"), chatInput = document.querySelector("#chat-input"), chatUnread = document.querySelector("#chat-unread"); const roomDetails = document.querySelector("#room-details"), roomUsers = document.querySelector("#room-users"), roomCount = document.querySelector("#room-count"), socketLatency = document.querySelector("#socket-latency"), chatMessages = document.querySelector("#chat-messages"), chatForm = document.querySelector("#chat-form"), chatInput = document.querySelector("#chat-input"), chatUnread = document.querySelector("#chat-unread");
let unreadChat = 0; let unreadChat = 0;
@@ -56,29 +56,25 @@ function renderGutter() {
const showSingleOwner = owners.length === 1; const showSingleOwner = owners.length === 1;
const showAuthorship = owners.length > 1; const showAuthorship = owners.length > 1;
const authorsByLine = showAuthorship ? lineAuthors(editor.value, authorship) : []; const authorsByLine = showAuthorship ? lineAuthors(editor.value, authorship) : [];
if (documentOwnerBadge) {
documentOwnerBadge.hidden = !showSingleOwner;
if (showSingleOwner) {
documentOwnerBadge.textContent = ownerName(owners[0]);
documentOwnerBadge.style.setProperty("--owner", colorFor(owners[0]));
} else {
documentOwnerBadge.textContent = "";
documentOwnerBadge.style.removeProperty("--owner");
}
}
authorshipLayer.hidden = !showAuthorship; authorshipLayer.hidden = !showAuthorship;
ownerLabels.hidden = !showAuthorship; ownerLabels.hidden = !(showSingleOwner || showAuthorship);
const style = getComputedStyle(editor), lineHeight = parseFloat(style.lineHeight) || 29, paddingTop = parseFloat(style.paddingTop) || 24, paddingBottom = parseFloat(style.paddingBottom) || 24; const style = getComputedStyle(editor), lineHeight = parseFloat(style.lineHeight) || 29, paddingTop = parseFloat(style.paddingTop) || 24, paddingBottom = parseFloat(style.paddingBottom) || 24;
gutter.style.paddingTop = `${paddingTop}px`; gutter.style.paddingBottom = `${paddingBottom}px`; gutter.style.lineHeight = `${lineHeight}px`; gutter.style.paddingTop = `${paddingTop}px`; gutter.style.paddingBottom = `${paddingBottom}px`; gutter.style.lineHeight = `${lineHeight}px`;
gutter.innerHTML = lines.map((_, i) => `<div style="height:${lineHeight}px">${i + 1}</div>`).join(""); gutter.innerHTML = lines.map((_, i) => `<div style="height:${lineHeight}px">${i + 1}</div>`).join("");
ownerLabels.style.setProperty("--editor-line-height", `${lineHeight}px`); ownerLabels.style.setProperty("--editor-line-height", `${lineHeight}px`);
ownerLabels.innerHTML = lines.map((_, i) => { if (showSingleOwner) {
const authors = authorsByLine[i] || []; const owner = owners[0];
if (!authors.length) return ""; const top = paddingTop - editor.scrollTop;
const top = paddingTop + i * lineHeight - editor.scrollTop; ownerLabels.innerHTML = `<span class="owner-label-group" style="top:${top}px"><span class="owner-label" style="--owner:${colorFor(owner)}">${escapeHtml(ownerName(owner))}</span></span>`;
const badges = authors.map(owner => `<span class="owner-label" style="--owner:${colorFor(owner)}">${escapeHtml(ownerName(owner))}</span>`).join(""); } else {
return `<span class="owner-line" style="top:${top}px;--owner:${colorFor(authors[0])}"></span><span class="owner-label-group" style="top:${top}px">${badges}</span>`; ownerLabels.innerHTML = lines.map((_, i) => {
}).join(""); const authors = authorsByLine[i] || [];
if (!authors.length) return "";
const top = paddingTop + i * lineHeight - editor.scrollTop;
const badges = authors.map(owner => `<span class="owner-label" style="--owner:${colorFor(owner)}">${escapeHtml(ownerName(owner))}</span>`).join("");
return `<span class="owner-line" style="top:${top}px;--owner:${colorFor(authors[0])}"></span><span class="owner-label-group" style="top:${top}px">${badges}</span>`;
}).join("");
}
if (showAuthorship) renderAuthorshipLayer(authorshipLayer, editor, authorship, colorFor); if (showAuthorship) renderAuthorshipLayer(authorshipLayer, editor, authorship, colorFor);
else authorshipLayer.replaceChildren(); else authorshipLayer.replaceChildren();
document.body.classList.toggle("hide-editor-line-numbers", !lineToggle.checked); document.body.classList.toggle("hide-editor-line-numbers", !lineToggle.checked);
+15 -19
View File
@@ -14,7 +14,7 @@ import { PadSocket } from "@rustpad/socket";
import { currentShareUrl, readEditorState, writeEditorState } from "@rustpad/url-state"; import { currentShareUrl, readEditorState, writeEditorState } from "@rustpad/url-state";
const slug = location.pathname.split("/").filter(Boolean)[1]; const slug = location.pathname.split("/").filter(Boolean)[1];
const editor = document.querySelector("#editor"), preview = document.querySelector("#preview"), editorWorkspace = document.querySelector("#editor-workspace"), gutter = document.querySelector("#line-gutter"), ownerLabels = document.querySelector("#owner-labels"), authorshipLayer = document.querySelector("#authorship-layer"), documentOwnerBadge = document.querySelector("#document-owner-badge"); const editor = document.querySelector("#editor"), preview = document.querySelector("#preview"), editorWorkspace = document.querySelector("#editor-workspace"), gutter = document.querySelector("#line-gutter"), ownerLabels = document.querySelector("#owner-labels"), authorshipLayer = document.querySelector("#authorship-layer");
const modeToggle = document.querySelector("#mode-toggle"), passwordDialog = document.querySelector("#password-dialog"), identityDialog = document.querySelector("#identity-dialog"); const modeToggle = document.querySelector("#mode-toggle"), passwordDialog = document.querySelector("#password-dialog"), identityDialog = document.querySelector("#identity-dialog");
const roomDetails = document.querySelector("#room-details"), roomUsers = document.querySelector("#room-users"), roomCount = document.querySelector("#room-count"), socketLatency = document.querySelector("#socket-latency"), chatMessages = document.querySelector("#chat-messages"), chatForm = document.querySelector("#chat-form"), chatInput = document.querySelector("#chat-input"), chatUnread = document.querySelector("#chat-unread"); const roomDetails = document.querySelector("#room-details"), roomUsers = document.querySelector("#room-users"), roomCount = document.querySelector("#room-count"), socketLatency = document.querySelector("#socket-latency"), chatMessages = document.querySelector("#chat-messages"), chatForm = document.querySelector("#chat-form"), chatInput = document.querySelector("#chat-input"), chatUnread = document.querySelector("#chat-unread");
let unreadChat = 0; let unreadChat = 0;
@@ -54,29 +54,25 @@ function renderGutter() {
const showSingleOwner = owners.length === 1; const showSingleOwner = owners.length === 1;
const showAuthorship = owners.length > 1; const showAuthorship = owners.length > 1;
const authorsByLine = showAuthorship ? lineAuthors(editor.value, authorship) : []; const authorsByLine = showAuthorship ? lineAuthors(editor.value, authorship) : [];
if (documentOwnerBadge) {
documentOwnerBadge.hidden = !showSingleOwner;
if (showSingleOwner) {
documentOwnerBadge.textContent = ownerName(owners[0]);
documentOwnerBadge.style.setProperty("--owner", colorFor(owners[0]));
} else {
documentOwnerBadge.textContent = "";
documentOwnerBadge.style.removeProperty("--owner");
}
}
authorshipLayer.hidden = !showAuthorship; authorshipLayer.hidden = !showAuthorship;
ownerLabels.hidden = !showAuthorship; ownerLabels.hidden = !(showSingleOwner || showAuthorship);
const style = getComputedStyle(editor), lineHeight = parseFloat(style.lineHeight) || 29, paddingTop = parseFloat(style.paddingTop) || 24, paddingBottom = parseFloat(style.paddingBottom) || 24; const style = getComputedStyle(editor), lineHeight = parseFloat(style.lineHeight) || 29, paddingTop = parseFloat(style.paddingTop) || 24, paddingBottom = parseFloat(style.paddingBottom) || 24;
gutter.style.paddingTop = `${paddingTop}px`; gutter.style.paddingBottom = `${paddingBottom}px`; gutter.style.lineHeight = `${lineHeight}px`; gutter.style.paddingTop = `${paddingTop}px`; gutter.style.paddingBottom = `${paddingBottom}px`; gutter.style.lineHeight = `${lineHeight}px`;
gutter.innerHTML = lines.map((_, i) => `<div style="height:${lineHeight}px">${i + 1}</div>`).join(""); gutter.innerHTML = lines.map((_, i) => `<div style="height:${lineHeight}px">${i + 1}</div>`).join("");
ownerLabels.style.setProperty("--editor-line-height", `${lineHeight}px`); ownerLabels.style.setProperty("--editor-line-height", `${lineHeight}px`);
ownerLabels.innerHTML = lines.map((_, i) => { if (showSingleOwner) {
const authors = authorsByLine[i] || []; const owner = owners[0];
if (!authors.length) return ""; const top = paddingTop - editor.scrollTop;
const top = paddingTop + i * lineHeight - editor.scrollTop; ownerLabels.innerHTML = `<span class="owner-label-group" style="top:${top}px"><span class="owner-label" style="--owner:${colorFor(owner)}">${escapeHtml(ownerName(owner))}</span></span>`;
const badges = authors.map(owner => `<span class="owner-label" style="--owner:${colorFor(owner)}">${escapeHtml(ownerName(owner))}</span>`).join(""); } else {
return `<span class="owner-line" style="top:${top}px;--owner:${colorFor(authors[0])}"></span><span class="owner-label-group" style="top:${top}px">${badges}</span>`; ownerLabels.innerHTML = lines.map((_, i) => {
}).join(""); const authors = authorsByLine[i] || [];
if (!authors.length) return "";
const top = paddingTop + i * lineHeight - editor.scrollTop;
const badges = authors.map(owner => `<span class="owner-label" style="--owner:${colorFor(owner)}">${escapeHtml(ownerName(owner))}</span>`).join("");
return `<span class="owner-line" style="top:${top}px;--owner:${colorFor(authors[0])}"></span><span class="owner-label-group" style="top:${top}px">${badges}</span>`;
}).join("");
}
if (showAuthorship) renderAuthorshipLayer(authorshipLayer, editor, authorship, colorFor); if (showAuthorship) renderAuthorshipLayer(authorshipLayer, editor, authorship, colorFor);
else authorshipLayer.replaceChildren(); else authorshipLayer.replaceChildren();
document.body.classList.toggle("hide-editor-line-numbers", !lineToggle.checked); document.body.classList.toggle("hide-editor-line-numbers", !lineToggle.checked);
+1 -1
View File
@@ -97,7 +97,7 @@
</div> </div>
<div id="editor-workspace" class="workspace view-split"> <div id="editor-workspace" class="workspace view-split">
<div class="editor-column"> <div class="editor-column">
<div class="column-label editor-column-label"><span>Editor</span><span id="document-owner-badge" class="document-owner-badge" hidden></span></div> <div class="column-label editor-column-label"><span>Editor</span></div>
<div class="editor-shell"> <div class="editor-shell">
<div id="line-gutter" class="line-gutter" aria-hidden="true"></div> <div id="line-gutter" class="line-gutter" aria-hidden="true"></div>
<div id="authorship-layer" class="authorship-layer" aria-hidden="true"></div><div id="owner-labels" class="owner-labels" aria-hidden="true"></div><textarea id="editor" <div id="authorship-layer" class="authorship-layer" aria-hidden="true"></div><div id="owner-labels" class="owner-labels" aria-hidden="true"></div><textarea id="editor"
+1 -1
View File
@@ -95,7 +95,7 @@
</div> </div>
<div id="editor-workspace" class="workspace view-split"> <div id="editor-workspace" class="workspace view-split">
<div class="editor-column"> <div class="editor-column">
<div class="column-label editor-column-label"><span>Editor</span><span id="document-owner-badge" class="document-owner-badge" hidden></span></div> <div class="column-label editor-column-label"><span>Editor</span></div>
<div class="editor-shell"> <div class="editor-shell">
<div id="line-gutter" class="line-gutter" aria-hidden="true"></div> <div id="line-gutter" class="line-gutter" aria-hidden="true"></div>
<div id="authorship-layer" class="authorship-layer" aria-hidden="true"></div><div id="owner-labels" class="owner-labels" aria-hidden="true"></div><textarea id="editor" <div id="authorship-layer" class="authorship-layer" aria-hidden="true"></div><div id="owner-labels" class="owner-labels" aria-hidden="true"></div><textarea id="editor"