fixes
This commit is contained in:
@@ -11,3 +11,4 @@ README.md
|
||||
Dockerfile*
|
||||
docker-compose*.yml
|
||||
migrate/
|
||||
scripts/*.txt
|
||||
+2
-1
@@ -13,4 +13,5 @@ data/files/*
|
||||
venv
|
||||
.venv
|
||||
migrate/etherpad-dry-run-report.json
|
||||
data/garge
|
||||
data/garage
|
||||
scripts/*.txt
|
||||
Generated
+1
-1
@@ -2433,7 +2433,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustpad"
|
||||
version = "0.0.15"
|
||||
version = "0.0.17"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"aws-config",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustpad"
|
||||
version = "0.0.16"
|
||||
version = "0.0.17"
|
||||
edition = "2024"
|
||||
rust-version = "1.94"
|
||||
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# scripts/random_text_generator.py
|
||||
|
||||
import random
|
||||
import string
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def anonymize_text(text: str) -> str:
|
||||
result = []
|
||||
|
||||
for character in text:
|
||||
if character.islower():
|
||||
result.append(random.choice(string.ascii_lowercase))
|
||||
elif character.isupper():
|
||||
result.append(random.choice(string.ascii_uppercase))
|
||||
elif character.isdigit():
|
||||
result.append(random.choice(string.digits))
|
||||
else:
|
||||
result.append(character)
|
||||
|
||||
return "".join(result)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python scripts/random_text_generator.py <input_file>")
|
||||
sys.exit(1)
|
||||
|
||||
input_path = Path(sys.argv[1])
|
||||
|
||||
if not input_path.is_file():
|
||||
print(f"File not found: {input_path}")
|
||||
sys.exit(1)
|
||||
|
||||
source_text = input_path.read_text(encoding="utf-8")
|
||||
anonymized_text = anonymize_text(source_text)
|
||||
|
||||
output_path = input_path.with_name(
|
||||
f"{input_path.stem}_anonymized{input_path.suffix}"
|
||||
)
|
||||
|
||||
output_path.write_text(anonymized_text, encoding="utf-8")
|
||||
|
||||
print(f"Anonymized file saved to: {output_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+67
-36
@@ -2201,7 +2201,7 @@ dialog::backdrop {
|
||||
content: attr(data-source-line);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -50px;
|
||||
left: var(--preview-line-left, -50px);
|
||||
width: 32px;
|
||||
color: #596270;
|
||||
text-align: right;
|
||||
@@ -2304,70 +2304,80 @@ dialog::backdrop {
|
||||
color: #b9c2cf;
|
||||
}
|
||||
|
||||
.markdown-body .task-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none
|
||||
}
|
||||
|
||||
/* Task-list layout. */
|
||||
.markdown-body .task-list-item {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1em minmax(0, 1fr);
|
||||
grid-template-rows: 1.32em;
|
||||
grid-template-rows: auto auto;
|
||||
column-gap: .45em;
|
||||
align-items: center;
|
||||
align-items: start;
|
||||
min-height: 1.32em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1.32
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: calc(100% + 18px);
|
||||
width: 32px;
|
||||
height: 1.32em;
|
||||
line-height: 1.32em;
|
||||
transform: none
|
||||
line-height: 1.32;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.markdown-body .task-checkbox {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
align-self: start;
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
flex: 0 0 1em;
|
||||
width: 1em;
|
||||
min-width: 1em;
|
||||
max-width: 1em;
|
||||
height: 1em;
|
||||
margin: 0;
|
||||
align-self: center;
|
||||
accent-color: var(--accent);
|
||||
cursor: pointer
|
||||
min-height: 1em;
|
||||
max-height: 1em;
|
||||
margin: .14em 0 0;
|
||||
padding: 0;
|
||||
border: 1px solid #707887;
|
||||
border-radius: 2px;
|
||||
background: var(--surface-2);
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item>span {
|
||||
.markdown-body .task-checkbox:checked {
|
||||
border-color: var(--accent);
|
||||
background-color: var(--accent);
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2 6.5 4.7 9 10 3'/%3E%3C/svg%3E");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: .78em .78em;
|
||||
}
|
||||
|
||||
.markdown-body .task-checkbox:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.markdown-body .task-checkbox:focus-visible {
|
||||
box-shadow: 0 0 0 2px rgba(124, 104, 238, .3);
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item > .list-item-content {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
display: block;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1.32
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.hide-preview-line-numbers .markdown-body .task-list-item::before {
|
||||
display: none
|
||||
.markdown-body .task-list-item > ul,
|
||||
.markdown-body .task-list-item > ol {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.compact-editor .markdown-body .task-list-item {
|
||||
grid-template-rows: 1.24em;
|
||||
min-height: 1.24em;
|
||||
line-height: 1.24
|
||||
}
|
||||
|
||||
.compact-editor .markdown-body .task-list-item::before,
|
||||
.compact-editor .markdown-body .task-list-item>span {
|
||||
height: 1.24em;
|
||||
line-height: 1.24
|
||||
line-height: 1.24;
|
||||
}
|
||||
|
||||
.markdown-body .footnotes {
|
||||
@@ -3805,3 +3815,24 @@ dialog::backdrop {
|
||||
.markdown-body .markdown-toc .toc-level-4,
|
||||
.markdown-body .markdown-toc .toc-level-5,
|
||||
.markdown-body .markdown-toc .toc-level-6 { margin-left: 3em; }
|
||||
|
||||
/* Nested Markdown lists keep markers and source-line numbers in separate gutters. */
|
||||
.markdown-body ul,
|
||||
.markdown-body ol {
|
||||
padding-left: 1.75em;
|
||||
}
|
||||
|
||||
.markdown-body .list-source-line::before {
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.markdown-body .list-item-content {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.markdown-body .contains-task-items > .task-list-item {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
+77
-18
@@ -92,6 +92,63 @@ function normalizeLanguage(value) {
|
||||
const attrs = (line, editable = false, prefix = "", suffix = "", lineOffset = 0) => ` class="preview-source-line${editable ? " preview-editable" : ""}" data-source-line="${line + lineOffset + 1}"${editable ? ` contenteditable="true" spellcheck="true" data-source-prefix="${escapeHtml(prefix)}" data-source-suffix="${escapeHtml(suffix)}"` : ""}`;
|
||||
const isPlainText = line => !/[`*_~^=\[\]<>|:#]/.test(line) && !/^\s*(?:[-+*>]|\d+\.)\s/.test(line);
|
||||
|
||||
|
||||
function listLine(line) {
|
||||
const match = line.match(/^(\s*)([-*+]|(\d+)\.)\s+(?:\[([ xX])\]\s+)?(.+)$/);
|
||||
if (!match) return null;
|
||||
const indent = match[1].replace(/\t/g, " ").length;
|
||||
return {
|
||||
indent,
|
||||
whitespace: match[1],
|
||||
type: match[3] ? "ol" : "ul",
|
||||
number: match[3] ? Number(match[3]) : null,
|
||||
checked: match[4] == null ? null : match[4].toLowerCase() === "x",
|
||||
text: match[5],
|
||||
marker: match[3] ? `${match[3]}. ` : `${match[2]} `
|
||||
};
|
||||
}
|
||||
|
||||
function renderList(lines, start, lineOffset = 0, baseIndent = null, forcedType = null, depth = 0) {
|
||||
const first = listLine(lines[start]);
|
||||
if (!first) return null;
|
||||
const indent = baseIndent == null ? first.indent : baseIndent;
|
||||
const type = forcedType || first.type;
|
||||
let index = start;
|
||||
let body = "";
|
||||
let hasTask = false;
|
||||
|
||||
while (index < lines.length) {
|
||||
const item = listLine(lines[index]);
|
||||
if (!item || item.indent < indent || item.indent !== indent || item.type !== type) break;
|
||||
|
||||
const sourceLine = index + lineOffset + 1;
|
||||
const prefix = `${item.whitespace}${item.marker}${item.checked == null ? "" : `[${item.checked ? "x" : " "}] `}`;
|
||||
const valueAttr = "";
|
||||
const taskClass = item.checked == null ? "" : " task-list-item";
|
||||
hasTask ||= item.checked != null;
|
||||
const gutterOffset = `${(depth + 1) * 1.75}em`;
|
||||
body += `<li class="preview-source-line list-source-line${taskClass}" data-source-line="${sourceLine}"${valueAttr} style="--list-gutter-offset:${gutterOffset}">`;
|
||||
if (item.checked != null) {
|
||||
body += `<input type="checkbox" class="task-checkbox" data-source-line="${sourceLine}"${item.checked ? " checked" : ""}>`;
|
||||
}
|
||||
body += `<span class="preview-editable list-item-content" contenteditable="true" spellcheck="true" data-source-line="${sourceLine}" data-source-prefix="${escapeHtml(prefix)}">${inline(item.text)}</span>`;
|
||||
index++;
|
||||
|
||||
while (index < lines.length) {
|
||||
const nested = listLine(lines[index]);
|
||||
if (!nested || nested.indent <= indent) break;
|
||||
const rendered = renderList(lines, index, lineOffset, nested.indent, nested.type, depth + 1);
|
||||
if (!rendered) break;
|
||||
body += rendered.html;
|
||||
index = rendered.end;
|
||||
}
|
||||
body += `</li>`;
|
||||
}
|
||||
|
||||
const classAttr = hasTask ? ` class="contains-task-items"` : "";
|
||||
return {html: `<${type}${classAttr}>${body}</${type}>`, end: index};
|
||||
}
|
||||
|
||||
function splitTableRow(line) {
|
||||
let value = line.trim();
|
||||
if (value.startsWith("|")) value = value.slice(1);
|
||||
@@ -138,8 +195,20 @@ function collectHeadings(lines) {
|
||||
return headings;
|
||||
}
|
||||
|
||||
|
||||
export function alignPreviewLineNumbers(root) {
|
||||
if (!root) return;
|
||||
const styles = getComputedStyle(root);
|
||||
const targetLeft = parseFloat(styles.paddingLeft || "0") - 50;
|
||||
const rootLeft = root.getBoundingClientRect().left;
|
||||
root.querySelectorAll(".preview-source-line").forEach(line => {
|
||||
const lineLeft = line.getBoundingClientRect().left - rootLeft;
|
||||
line.style.setProperty("--preview-line-left", `${targetLeft - lineLeft}px`);
|
||||
});
|
||||
}
|
||||
|
||||
export function renderMarkdown(source, lineOffset = 0) {
|
||||
let html = "", inCode = false, fence = "", language = "", codeLineStart = null, code = [], codeStart = 0, list = null;
|
||||
let html = "", inCode = false, fence = "", language = "", codeLineStart = null, code = [], codeStart = 0;
|
||||
const lines = String(source).split("\n");
|
||||
const headings = collectHeadings(lines);
|
||||
const headingByLine = new Map(headings.map(item => [item.index, item]));
|
||||
@@ -159,7 +228,7 @@ export function renderMarkdown(source, lineOffset = 0) {
|
||||
lines[i] = "";
|
||||
}
|
||||
|
||||
const closeList = () => { if (list) { html += `</${list}>`; list = null; } };
|
||||
const closeList = () => {};
|
||||
const closeCode = () => {
|
||||
const body = escapeHtml(code.join("\n"));
|
||||
const lang = normalizeLanguage(language);
|
||||
@@ -261,29 +330,19 @@ export function renderMarkdown(source, lineOffset = 0) {
|
||||
}
|
||||
|
||||
const heading = line.match(/^\s{0,4}(#{1,6})\s+(.+?)(?:\s+\{#([A-Za-z][\w:.-]*)\})?\s*$/);
|
||||
const task = line.match(/^(\s*)[-*+]\s+\[([ xX])\]\s+(.+)$/);
|
||||
const ul = line.match(/^(\s*)[-*+]\s+(.+)$/);
|
||||
const ol = line.match(/^(\s*)\d+\.\s+(.+)$/);
|
||||
const listItem = listLine(line);
|
||||
|
||||
if (heading) {
|
||||
if (listItem) {
|
||||
const rendered = renderList(lines, index, lineOffset, listItem.indent, listItem.type);
|
||||
html += rendered.html;
|
||||
index = rendered.end - 1;
|
||||
} else if (heading) {
|
||||
closeList();
|
||||
const n = heading[1].length;
|
||||
const resolved = headingByLine.get(index);
|
||||
const id = resolved?.id || heading[3] || headingSlug(heading[2]);
|
||||
const suffix = heading[3] ? ` {#${heading[3]}}` : "";
|
||||
html += `<h${n} id="${escapeHtml(id)}"${attrs(index, true, `${heading[1]} `, suffix, lineOffset)}>${inline(heading[2])}</h${n}>`;
|
||||
} else if (task) {
|
||||
if (list !== "ul") { closeList(); html += `<ul class="task-list">`; list = "ul"; }
|
||||
const checked = task[2].toLowerCase() === "x";
|
||||
html += `<li class="preview-source-line task-list-item" data-source-line="${index + lineOffset + 1}"><input type="checkbox" class="task-checkbox" data-source-line="${index + lineOffset + 1}"${checked ? " checked" : ""}><span class="preview-editable" contenteditable="true" spellcheck="true" data-source-line="${index + lineOffset + 1}" data-source-prefix="${escapeHtml(`${task[1]}- [${checked ? "x" : " "}] `)}">${inline(task[3])}</span></li>`;
|
||||
} else if (ul || ol) {
|
||||
const type = ul ? "ul" : "ol";
|
||||
if (list !== type) { closeList(); html += `<${type}>`; list = type; }
|
||||
const match = ul || ol;
|
||||
const indentWidth = match[1].replace(/\t/g, " ").length;
|
||||
const prefix = ul ? `${match[1]}- ` : `${match[1]}${(line.match(/^\s*(\d+)\./)||[])[1] || 1}. `;
|
||||
const indentStyle = indentWidth ? ` style="margin-left:${Math.min(indentWidth, 24) * 0.45}em"` : "";
|
||||
html += `<li${attrs(index, true, prefix, "", lineOffset)}${indentStyle}>${inline(match[2])}</li>`;
|
||||
} else {
|
||||
closeList();
|
||||
const definition = index + 1 < lines.length && /^:\s+/.test(lines[index + 1]);
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ installGlobalDiagnostics();
|
||||
import { api } from "@rustpad/api";
|
||||
import { copyText } from "@rustpad/clipboard";
|
||||
import { applyFormat, bindFormatShortcuts } from "@rustpad/editor-format";
|
||||
import { renderMarkdown } from "@rustpad/markdown";
|
||||
import { alignPreviewLineNumbers, renderMarkdown } from "@rustpad/markdown";
|
||||
import { prepareImageFile } from "./image-upload.js";
|
||||
import { getNickname, getAccessToken, getAuthToken, setAccessToken } from "@rustpad/session";
|
||||
import { bindIdentityDialog } from "./auth-ui.js";
|
||||
@@ -132,7 +132,7 @@ function replaceTableCell(line,index,value){
|
||||
const cells=body.split("|").map(cell=>cell.trim());while(cells.length<=index)cells.push("");cells[index]=value.replace(/\|/g,"|");
|
||||
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="Markdown + Mermaid preview · text and headings are editable";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";}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 render(){if(uiState.mode==="markdown"){preview.classList.remove("preview--raw");preview.innerHTML=renderMarkdown(editor.value);document.querySelector("#preview-label").textContent="Markdown + Mermaid preview · text and headings are editable";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 applyUi({write=false,replace=false}={}){editorWorkspace.className=`workspace view-${uiState.view} editor-workspace-font-${fontFamily.value}`;editorWorkspace.style.setProperty("--editor-font-size",`${fontSize.value}px`);document.body.classList.toggle("compact-editor",compactToggle.checked);document.querySelectorAll("[data-view]").forEach(b=>{const a=b.dataset.view===uiState.view;b.classList.toggle("active",a);b.setAttribute("aria-pressed",String(a));});const markdown=uiState.mode==="markdown";modeToggle.classList.toggle("active",markdown);modeToggle.textContent=markdown?"Markdown":"Text";render();if(write)writeEditorState(uiState,{replace});updateAddressLabel();}
|
||||
function applyRemote(content,ownerMap){if(content===editor.value&&ownerMap==null)return;const start=editor.selectionStart,end=editor.selectionEnd;applyingRemote=true;editor.value=content;try{owners=JSON.parse(ownerMap||"[]");}catch{owners=[];}editor.setSelectionRange(Math.min(start,content.length),Math.min(end,content.length));applyingRemote=false;render();}
|
||||
function connect(){socket?.stop();socket=new NoteSocket({workspaceSlug,noteSlug,password,accessToken,nickname,color:currentUserColor()||null,sessionToken:getAuthToken(),onStatus:s=>setStatus(s==="online"?"online":s==="offline"?"offline":null,s==="online"?"Connected":s==="offline"?"Reconnecting…":"Connecting…"),onAuthenticated:m=>{if(passwordDialog.open)passwordDialog.close();applyRemote(m.content,m.owner_map);editor.focus();},onDocument:m=>{applyRemote(m.content,m.owner_map);document.querySelector("#save-state").textContent=`${m.author?`${m.author} · `:""}${new Date(m.updated_at).toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit"})}`;},onPresence:updatePresence,onLatency:updateLatency,onChat:appendChatMessage,onError:m=>{document.querySelector("#password-error").textContent=m;if(/nickname|session|account/i.test(m)){if(!identityDialog.open)identityDialog.showModal();}else if(info?.protected&&!passwordDialog.open)passwordDialog.showModal();}});socket.connect();}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ installGlobalDiagnostics();
|
||||
import { api } from "@rustpad/api";
|
||||
import { copyText } from "@rustpad/clipboard";
|
||||
import { applyFormat, bindFormatShortcuts } from "@rustpad/editor-format";
|
||||
import { renderMarkdown } from "@rustpad/markdown";
|
||||
import { alignPreviewLineNumbers, renderMarkdown } from "@rustpad/markdown";
|
||||
import { prepareImageFile } from "./image-upload.js";
|
||||
import { getNickname, getAuthToken, getAccessToken, setAccessToken } from "@rustpad/session";
|
||||
import { bindIdentityDialog } from "./auth-ui.js";
|
||||
@@ -128,7 +128,7 @@ function replaceTableCell(line,index,value){
|
||||
const cells=body.split("|").map(cell=>cell.trim());while(cells.length<=index)cells.push("");cells[index]=value.replace(/\|/g,"|");
|
||||
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="Markdown + Mermaid preview · text and headings are editable";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";}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 render(){if(uiState.mode==="markdown"){preview.classList.remove("preview--raw");preview.innerHTML=renderMarkdown(editor.value);document.querySelector("#preview-label").textContent="Markdown + Mermaid preview · text and headings are editable";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 applyUi({write=false,replace=false}={}){editorWorkspace.className=`workspace view-${uiState.view} editor-workspace-font-${fontFamily.value}`;editorWorkspace.style.setProperty("--editor-font-size",`${fontSize.value}px`);document.body.classList.toggle("compact-editor",compactToggle.checked);document.querySelectorAll("[data-view]").forEach(b=>{const a=b.dataset.view===uiState.view;b.classList.toggle("active",a);b.setAttribute("aria-pressed",String(a));});const markdown=uiState.mode==="markdown";modeToggle.classList.toggle("active",markdown);modeToggle.textContent=markdown?"Markdown":"Text";render();if(write)writeEditorState(uiState,{replace});updateAddressLabel();}
|
||||
function applyRemote(content,ownerMap){if(content===editor.value&&ownerMap==null)return;const start=editor.selectionStart,end=editor.selectionEnd;applyingRemote=true;editor.value=content;try{owners=JSON.parse(ownerMap||"[]");}catch{owners=[];}editor.setSelectionRange(Math.min(start,content.length),Math.min(end,content.length));applyingRemote=false;render();}
|
||||
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ installGlobalDiagnostics();
|
||||
|
||||
import { api } from "@rustpad/api";
|
||||
import { copyText } from "@rustpad/clipboard";
|
||||
import { renderMarkdown } from "@rustpad/markdown";
|
||||
import { alignPreviewLineNumbers, renderMarkdown } from "@rustpad/markdown";
|
||||
|
||||
const token=location.pathname.split("/").filter(Boolean)[1];
|
||||
const content=document.querySelector("#public-content");
|
||||
@@ -16,7 +16,7 @@ function lockPublicContent(allowTaskUpdates){
|
||||
content.querySelectorAll('.preview-editable').forEach(node=>node.classList.remove('preview-editable'));
|
||||
content.querySelectorAll('.task-checkbox').forEach(box=>{box.disabled=!allowTaskUpdates;box.title=allowTaskUpdates?'Update this task':'Task updates are disabled by the owner';});
|
||||
}
|
||||
async function initialize(){try{const page=await api(`/api/public/${encodeURIComponent(token)}`);document.querySelector("#public-title").textContent=page.title;document.querySelector("#public-meta").textContent=`Updated: ${new Date(page.updated_at).toLocaleString("en-US")}${page.allow_task_updates?" · tasks can be updated":""}`;document.title=`${page.title} · RustPad`;content.innerHTML=renderMarkdown(page.content);lockPublicContent(page.allow_task_updates);await Promise.all([renderMermaid(),renderCodeHighlight()]);}catch(error){content.innerHTML=`<p class="error">${String(error.message)}</p>`;}}
|
||||
async function initialize(){try{const page=await api(`/api/public/${encodeURIComponent(token)}`);document.querySelector("#public-title").textContent=page.title;document.querySelector("#public-meta").textContent=`Updated: ${new Date(page.updated_at).toLocaleString("en-US")}${page.allow_task_updates?" · tasks can be updated":""}`;document.title=`${page.title} · RustPad`;content.innerHTML=renderMarkdown(page.content);alignPreviewLineNumbers(content);lockPublicContent(page.allow_task_updates);await Promise.all([renderMermaid(),renderCodeHighlight()]);}catch(error){content.innerHTML=`<p class="error">${String(error.message)}</p>`;}}
|
||||
content.addEventListener("change",async event=>{const box=event.target.closest(".task-checkbox");if(!box||box.disabled)return;const previous=!box.checked;box.disabled=true;try{const page=await api(`/api/public/${encodeURIComponent(token)}/tasks`,{method:"POST",body:JSON.stringify({source_line:Number(box.dataset.sourceLine),checked:box.checked})});document.querySelector("#public-meta").textContent=`Updated: ${new Date(page.updated_at).toLocaleString("en-US")} · tasks can be updated`;toast("Task saved");}catch(error){box.checked=previous;toast(error.message);}finally{box.disabled=false;}});
|
||||
lineNumbersToggle.addEventListener("change",()=>{document.body.classList.toggle("hide-preview-line-numbers",!lineNumbersToggle.checked);});
|
||||
document.querySelector("#copy-public-link").addEventListener("click",async()=>{try{await copyText(location.href);toast("Link copied");}catch(error){toast(error.message);}});
|
||||
|
||||
Reference in New Issue
Block a user