From be374980d40f5048b16c651946e4a9632002eca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sat, 25 Jul 2026 18:12:41 +0200 Subject: [PATCH] line multi authors --- Cargo.lock | 2 +- Cargo.toml | 2 +- static/css/styles.css | 25 +++++++++++++++++++++++++ static/js/note.js | 16 ++++++++++++++-- static/js/pad.js | 16 ++++++++++++++-- static/note.html | 2 +- static/pad.html | 2 +- 7 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db804a2..a21d6ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2433,7 +2433,7 @@ dependencies = [ [[package]] name = "rustpad" -version = "0.0.22" +version = "0.0.24" dependencies = [ "argon2", "aws-config", diff --git a/Cargo.toml b/Cargo.toml index 1f7b367..569d289 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustpad" -version = "0.0.23" +version = "0.0.24" 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 c1c8e2b..3a08dbf 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -4058,6 +4058,31 @@ dialog::backdrop { } } + +.editor-column-label { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.document-owner-badge { + max-width: 50%; + overflow: hidden; + padding: 2px 8px; + border: 1px solid color-mix(in srgb, var(--owner) 58%, transparent); + border-radius: 999px; + background: color-mix(in srgb, var(--owner) 14%, transparent); + color: var(--text); + font-size: .7rem; + font-weight: 600; + line-height: 1.35; + text-overflow: ellipsis; + white-space: nowrap; +} + +.document-owner-badge[hidden] { display: none; } + /* Per-character authorship overlay. The textarea remains the editable surface. */ .authorship-layer { position: absolute; diff --git a/static/js/note.js b/static/js/note.js index 7faa180..b4f12b9 100644 --- a/static/js/note.js +++ b/static/js/note.js @@ -15,7 +15,7 @@ import { askConfirm } from "@rustpad/modal"; import { currentShareUrl, readEditorState, writeEditorState } from "@rustpad/url-state"; 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"); +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 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"); let unreadChat = 0; @@ -52,8 +52,20 @@ async function renderCodeHighlight() { const nodes = preview.querySelectorAll('p function renderGutter() { const lineCount = Math.max(1, (editor.value.match(/\n/g) || []).length + 1); const lines = Array.from({ length: lineCount }); - const showAuthorship = authorshipOwners(authorship).length > 1; + const owners = authorshipOwners(authorship); + const showSingleOwner = owners.length === 1; + const showAuthorship = owners.length > 1; 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; ownerLabels.hidden = !showAuthorship; const style = getComputedStyle(editor), lineHeight = parseFloat(style.lineHeight) || 29, paddingTop = parseFloat(style.paddingTop) || 24, paddingBottom = parseFloat(style.paddingBottom) || 24; diff --git a/static/js/pad.js b/static/js/pad.js index 4d24955..4a7425e 100644 --- a/static/js/pad.js +++ b/static/js/pad.js @@ -14,7 +14,7 @@ import { PadSocket } from "@rustpad/socket"; import { currentShareUrl, readEditorState, writeEditorState } from "@rustpad/url-state"; 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"); +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 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"); let unreadChat = 0; @@ -50,8 +50,20 @@ async function renderCodeHighlight() { const nodes = preview.querySelectorAll('p function renderGutter() { const lineCount = Math.max(1, (editor.value.match(/\n/g) || []).length + 1); const lines = Array.from({ length: lineCount }); - const showAuthorship = authorshipOwners(authorship).length > 1; + const owners = authorshipOwners(authorship); + const showSingleOwner = owners.length === 1; + const showAuthorship = owners.length > 1; 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; ownerLabels.hidden = !showAuthorship; const style = getComputedStyle(editor), lineHeight = parseFloat(style.lineHeight) || 29, paddingTop = parseFloat(style.paddingTop) || 24, paddingBottom = parseFloat(style.paddingBottom) || 24; diff --git a/static/note.html b/static/note.html index 43b09db..2b62d95 100644 --- a/static/note.html +++ b/static/note.html @@ -97,7 +97,7 @@
-
Editor
+
Editor