This commit is contained in:
Mateusz Gruszczyński
2026-07-28 16:09:15 +02:00
parent d722e8af71
commit d37f7f6ad2
8 changed files with 80 additions and 15 deletions
+2 -2
View File
@@ -136,8 +136,8 @@ async function loadResources() {
dialog.innerHTML = `<div class="share-panel">
<div class="share-panel-head"><div><h3>Share access</h3><p>Manage registered users and direct access links for <strong>${escapeHtml(item.title)}</strong>.</p></div><button class="dialog-close" type="button" data-cancel aria-label="Close share dialog">×</button></div>
<div class="share-dialog-body">
<section class="share-section"><div class="share-section-head"><div><h4>People with access</h4><p>Invite existing RustPad users by e-mail, or AD/LDAP login.</p></div></div><form data-user-share-form class="share-form-grid">
<label class="share-emails"><span>E-mail or user names</span><input name="recipients" type="text" placeholder="alice@example.com, jsmith, John Snow" autocomplete="off"></label>
<section class="share-section"><div class="share-section-head"><div><h4>People with access</h4><p>Invite existing RustPad users by e-mail or account/directory username.</p></div></div><form data-user-share-form class="share-form-grid">
<label class="share-emails"><span>E-mail or username</span><input name="recipients" type="text" placeholder="alice@example.com, jsmith" autocomplete="off"></label>
<label><span>Permission</span><select name="permission"><option value="ro">Read only</option><option value="rw">Read and write</option></select></label>
<button class="primary-button" type="submit">Grant access</button>
</form><div data-user-list class="share-list"></div></section>
+9 -2
View File
@@ -194,7 +194,14 @@ export function startNoteEditor(adapter) {
const cells = body.split("|").map(cell => cell.trim()); while (cells.length <= index) cells.push(""); cells[index] = value.replace(/\|/g, "&#124;");
return `${leading ? "| " : ""}${cells.join(" | ")}${trailing ? " |" : ""}`;
}
function render() { if (uiState.mode === "markdown") { preview.classList.remove("preview--raw"); preview.innerHTML = renderMarkdown(editor.value); document.querySelector("#preview-label").textContent = "Preview (mermaid / markdown)"; renderMermaid(); renderCodeHighlight(); } else { preview.classList.add("preview--raw"); preview.innerHTML = editor.value.split("\n").map((line, index) => `<div class="preview-source-line preview-editable" data-source-line="${index + 1}" contenteditable="true" spellcheck="true">${escapeHtml(line) || "<br>"}</div>`).join(""); document.querySelector("#preview-label").textContent = "Text preview · editable"; } alignPreviewLineNumbers(preview); document.querySelector("#characters").textContent = `${editor.value.length} characters`; document.querySelector("#words").textContent = `${editor.value.trim() ? editor.value.trim().split(/\s+/).length : 0} words`; renderGutter(); }
function syncPreviewScroll() {
if (activeView() !== "split") return;
const editorRange = Math.max(0, editor.scrollHeight - editor.clientHeight);
const previewRange = Math.max(0, preview.scrollHeight - preview.clientHeight);
const ratio = editorRange > 0 ? editor.scrollTop / editorRange : 0;
preview.scrollTop = ratio * previewRange;
}
function render() { if (uiState.mode === "markdown") { preview.classList.remove("preview--raw"); preview.innerHTML = renderMarkdown(editor.value); document.querySelector("#preview-label").textContent = "Preview (mermaid / markdown)"; renderMermaid(); renderCodeHighlight(); } else { preview.classList.add("preview--raw"); preview.innerHTML = editor.value.split("\n").map((line, index) => `<div class="preview-source-line preview-editable" data-source-line="${index + 1}" contenteditable="true" spellcheck="true">${escapeHtml(line) || "<br>"}</div>`).join(""); document.querySelector("#preview-label").textContent = "Text preview · editable"; } alignPreviewLineNumbers(preview); document.querySelector("#characters").textContent = `${editor.value.length} characters`; document.querySelector("#words").textContent = `${editor.value.trim() ? editor.value.trim().split(/\s+/).length : 0} words`; renderGutter(); requestAnimationFrame(syncPreviewScroll); }
function activeView() {
return compactLayoutQuery.matches ? compactView : uiState.view;
}
@@ -390,7 +397,7 @@ export function startNoteEditor(adapter) {
if (socket) socket.update(editor.value, serializeAuthorship(authorship, editor.value.length));
toast("Global profile color restored");
});
editor.addEventListener("keydown", continueIndentation); editor.addEventListener("scroll", () => { gutter.scrollTop = editor.scrollTop; authorshipLayer.scrollTop = editor.scrollTop; authorshipLayer.scrollLeft = editor.scrollLeft; renderGutter(); }); editor.addEventListener("input", () => { const nextContent = editor.value; authorship = adoptCurrentOwnerAliases(authorship, previousContent.length); authorship = replaceAuthorshipOwner(authorship, owner => ownerName(owner) === nickname, currentOwner(), previousContent.length); authorship = applyAuthorshipEdit(authorship, previousContent, nextContent, currentOwner()); previousContent = nextContent; render(); if (applyingRemote) return; clearTimeout(saveTimer); document.querySelector("#save-state").textContent = "Saving…"; saveTimer = setTimeout(() => socket?.update(editor.value, serializeAuthorship(authorship, editor.value.length)), 250); });
editor.addEventListener("keydown", continueIndentation); editor.addEventListener("scroll", () => { gutter.scrollTop = editor.scrollTop; authorshipLayer.scrollTop = editor.scrollTop; authorshipLayer.scrollLeft = editor.scrollLeft; renderGutter(); syncPreviewScroll(); }); editor.addEventListener("input", () => { const nextContent = editor.value; authorship = adoptCurrentOwnerAliases(authorship, previousContent.length); authorship = replaceAuthorshipOwner(authorship, owner => ownerName(owner) === nickname, currentOwner(), previousContent.length); authorship = applyAuthorshipEdit(authorship, previousContent, nextContent, currentOwner()); previousContent = nextContent; render(); if (applyingRemote) return; clearTimeout(saveTimer); document.querySelector("#save-state").textContent = "Saving…"; saveTimer = setTimeout(() => socket?.update(editor.value, serializeAuthorship(authorship, editor.value.length)), 250); });
passwordDialog.addEventListener("cancel", event => {
if (info?.protected && !resourceUnlocked) {
event.preventDefault();