some changes
This commit is contained in:
@@ -25,14 +25,14 @@ import { toast } from "@rustpad/toast";
|
||||
export function startNoteEditor(adapter) {
|
||||
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 accessLevel = document.querySelector("#access-level"), 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"), mobileChatUnread = document.querySelector("#mobile-chat-unread");
|
||||
const accessLevel = document.querySelector("#access-level"), 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"), mobileChatUnread = document.querySelector("#mobile-chat-unread"), connectionNotice = document.querySelector("#connection-notice"), connectionNoticeTitle = document.querySelector("#connection-notice-title"), connectionNoticeMessage = document.querySelector("#connection-notice-message");
|
||||
let unreadChat = 0;
|
||||
const compactToggle = document.querySelector("#compact-toggle"), lineLinksToggle = document.querySelector("#line-links-toggle"), authorshipColorsToggle = document.querySelector("#authorship-colors-toggle"), authorshipColorsLabel = document.querySelector("#authorship-colors-label"), publicPageEnabled = document.querySelector("#public-page-enabled"), publicTaskUpdates = document.querySelector("#public-task-updates"), unprotectPublicPage = document.querySelector("#unprotect-public-page"), participantBadges = document.querySelector("#participant-badges"), fontFamily = document.querySelector("#font-family"), fontSize = document.querySelector("#font-size"), currentUser = document.querySelector("#current-user"), userColorPicker = document.querySelector("#user-color-picker"), mobileColorPicker = document.querySelector("#mobile-color-picker"), useGlobalColorButton = document.querySelector("#use-global-color");
|
||||
const mobileFontFamily = document.querySelector("#mobile-font-family"), mobileFontSize = document.querySelector("#mobile-font-size"), mobileLineToggle = document.querySelector("#mobile-line-numbers-toggle"), mobilePreviewLineToggle = document.querySelector("#mobile-preview-line-numbers-toggle"), mobileCompactToggle = document.querySelector("#mobile-compact-toggle"), mobileLineLinksToggle = document.querySelector("#mobile-line-links-toggle");
|
||||
const shareToken = new URLSearchParams(location.search).get("share");
|
||||
const notePreferenceKey = name => `rustpad:${name}:${location.pathname}`;
|
||||
let accessToken = shareToken || getAccessToken(adapter.access.kind, adapter.access.key), password = "", nickname = getNickname(), info, socket, saveTimer, applyingRemote = false, resourceUnlocked = false, uiState = readEditorState(), authorship = parseAuthorship("", "[]"), previousContent = "", globalColor = "", noteColor = "", presenceUsers = [], authorshipMode = "simple", authorshipColorsEnabled = true, lastRevealedLineHash = "";
|
||||
let editorSettingsSaveTimer, editorSettingsSaveInFlight = false, pendingPersonalSettingsSave = false, pendingAuthorshipSettingsSave = false;
|
||||
let editorSettingsSaveTimer, editorSettingsSaveInFlight = false, pendingPersonalSettingsSave = false, pendingAuthorshipSettingsSave = false, connectionNoticeTimer = 0, connectionWasInterrupted = false;
|
||||
const compactLayoutQuery = window.matchMedia("(max-width: 1499px)");
|
||||
const singlePaneQuery = window.matchMedia("(max-width: 760px)");
|
||||
let compactView = uiState.view === "preview" ? "preview" : "edit";
|
||||
@@ -149,6 +149,38 @@ export function startNoteEditor(adapter) {
|
||||
function clearUnread() { unreadChat = 0; chatUnread.hidden = true; chatUnread.textContent = ""; if (mobileChatUnread) { mobileChatUnread.hidden = true; mobileChatUnread.textContent = ""; } document.title = document.title.replace(/^● /, ""); }
|
||||
|
||||
function setStatus(kind, text) { const className = `status__dot${kind ? ` is-${kind}` : ""}`; document.querySelector("#status-dot").className = className; document.querySelector("#status-text").textContent = text; const mobileDot = document.querySelector("#mobile-status-dot"); const mobileText = document.querySelector("#mobile-status-text"); if (mobileDot) mobileDot.className = className; if (mobileText) mobileText.textContent = text; }
|
||||
function showConnectionNotice(title, message, restored = false) {
|
||||
clearTimeout(connectionNoticeTimer);
|
||||
connectionNoticeTitle.textContent = title;
|
||||
connectionNoticeMessage.textContent = message;
|
||||
connectionNotice.hidden = false;
|
||||
connectionNotice.classList.toggle("is-restored", restored);
|
||||
requestAnimationFrame(() => connectionNotice.classList.add("is-visible"));
|
||||
if (restored) connectionNoticeTimer = window.setTimeout(() => {
|
||||
connectionNotice.classList.remove("is-visible", "is-restored");
|
||||
connectionNoticeTimer = window.setTimeout(() => { connectionNotice.hidden = true; }, 220);
|
||||
}, 1800);
|
||||
}
|
||||
function hideConnectionNotice() {
|
||||
clearTimeout(connectionNoticeTimer);
|
||||
connectionNotice.classList.remove("is-visible", "is-restored");
|
||||
connectionNotice.hidden = true;
|
||||
}
|
||||
function handleSocketStatus(status, details = {}) {
|
||||
if (status === "online") {
|
||||
setStatus("online", "Connected");
|
||||
if (connectionWasInterrupted || details.restored) showConnectionNotice("Connection restored", "Live editing is active again.", true);
|
||||
connectionWasInterrupted = false;
|
||||
return;
|
||||
}
|
||||
if (status === "reconnecting") {
|
||||
connectionWasInterrupted = true;
|
||||
setStatus("offline", "Reconnecting…");
|
||||
showConnectionNotice("Connection interrupted", details.message || "Trying to reconnect automatically.");
|
||||
return;
|
||||
}
|
||||
setStatus(null, "Connecting…");
|
||||
}
|
||||
function updateAddressLabel() { document.querySelector(adapter.addressSelector).textContent = `${location.pathname}${location.search}`; }
|
||||
async function renderMermaid() { const nodes = preview.querySelectorAll(".mermaid"); if (!nodes.length) return; try { const { default: mermaid } = await import("https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs"); mermaid.initialize({ startOnLoad: false, theme: "dark", securityLevel: "strict" }); await mermaid.run({ nodes: [...nodes] }); } catch { nodes.forEach(n => n.insertAdjacentHTML("beforebegin", '<p class="error">Failed to load Mermaid.</p>')); } }
|
||||
async function renderCodeHighlight() { const nodes = preview.querySelectorAll('pre code[class^="language-"]:not(.language-mermaid)'); if (!nodes.length) return; try { const hljs = await import("https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/+esm"); nodes.forEach(node => { const lines = node.querySelectorAll(".code-line"); if (!lines.length) { hljs.default.highlightElement(node); return; } const language = [...node.classList].find(name => name.startsWith("language-"))?.slice(9); lines.forEach(line => { try { line.innerHTML = hljs.default.highlight(line.textContent, { language, ignoreIllegals: true }).value; } catch { line.innerHTML = hljs.default.highlightAuto(line.textContent).value; } }); node.classList.add("hljs"); }); } catch { } }
|
||||
@@ -619,7 +651,51 @@ export function startNoteEditor(adapter) {
|
||||
onFilesChanged: files => updateMarkdownFiles(files, { rerender: true }),
|
||||
});
|
||||
refreshFilesForAliases = () => loadFiles();
|
||||
function connect() { socket?.stop(); socket = adapter.createSocket({ password, accessToken, nickname, color: currentUserColor() || null, sessionToken: null, guestId: getGuestId(), onStatus: s => setStatus(s === "online" ? "online" : s === "offline" ? "offline" : null, s === "online" ? "Connected" : s === "offline" ? "Reconnecting…" : "Connecting…"), onAuthenticated: m => { resourceUnlocked = true; if (passwordDialog.open) passwordDialog.close(); const readOnly = m.access_level === "read_only"; editor.readOnly = readOnly; accessLevel.textContent = readOnly ? "Access: read only" : "Access: full"; applyRemote(m.content, m.owner_map); if (!readOnly) 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 => { const friendly = /read-only access/i.test(m) ? "This note is read only. Enter the password or ask the owner to grant write access." : m; document.querySelector("#password-error").textContent = friendly; if (/read-only access/i.test(m)) { toast(friendly); accessLevel.textContent = "Access: read only"; editor.readOnly = true; return; } if (/nickname|session|account/i.test(m)) { if (!identityDialog.open) identityDialog.showModal(); } else if (info?.protected && !passwordDialog.open) passwordDialog.showModal(); } }); socket.connect(); }
|
||||
function connect() {
|
||||
socket?.stop();
|
||||
socket = adapter.createSocket({
|
||||
password,
|
||||
accessToken,
|
||||
nickname,
|
||||
color: currentUserColor() || null,
|
||||
sessionToken: null,
|
||||
guestId: getGuestId(),
|
||||
onStatus: handleSocketStatus,
|
||||
onAuthenticated: message => {
|
||||
resourceUnlocked = true;
|
||||
if (passwordDialog.open) passwordDialog.close();
|
||||
const readOnly = message.access_level === "read_only";
|
||||
editor.readOnly = readOnly;
|
||||
accessLevel.textContent = readOnly ? "Access: read only" : "Access: full";
|
||||
applyRemote(message.content, message.owner_map);
|
||||
if (!readOnly) editor.focus();
|
||||
},
|
||||
onDocument: message => {
|
||||
applyRemote(message.content, message.owner_map);
|
||||
document.querySelector("#save-state").textContent = `${message.author ? `${message.author} · ` : ""}${new Date(message.updated_at).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" })}`;
|
||||
},
|
||||
onPresence: updatePresence,
|
||||
onLatency: updateLatency,
|
||||
onChat: appendChatMessage,
|
||||
onError: message => {
|
||||
hideConnectionNotice();
|
||||
const friendly = /read-only access/i.test(message) ? "This note is read only. Enter the password or ask the owner to grant write access." : message;
|
||||
document.querySelector("#password-error").textContent = friendly;
|
||||
if (/read-only access/i.test(message)) {
|
||||
toast(friendly);
|
||||
accessLevel.textContent = "Access: read only";
|
||||
editor.readOnly = true;
|
||||
return;
|
||||
}
|
||||
if (/nickname|session|account/i.test(message)) {
|
||||
if (!identityDialog.open) identityDialog.showModal();
|
||||
} else if (info?.protected && !passwordDialog.open) {
|
||||
passwordDialog.showModal();
|
||||
}
|
||||
},
|
||||
});
|
||||
socket.connect();
|
||||
}
|
||||
bindIdentityDialog({ dialog: identityDialog, onIdentity: async value => { nickname = value; accessToken = shareToken || getAccessToken(adapter.access.kind, adapter.access.key); identityDialog.close(); updateCurrentUser(); await loadNoteInfo(); if (info.protected && !accessToken && !getAuthToken()) passwordDialog.showModal(); else { loadFiles(); connect(); } } });
|
||||
identityDialog.addEventListener("close", () => { if (!nickname) queueMicrotask(() => { if (!identityDialog.open) identityDialog.showModal(); }); });
|
||||
async function showSystemNotFound() {
|
||||
@@ -698,6 +774,15 @@ export function startNoteEditor(adapter) {
|
||||
compactLayoutQuery.addEventListener("change", event => {
|
||||
if (!event.matches) setHeaderMenuOpen(false);
|
||||
});
|
||||
const mobileEditorOptions = document.querySelector("#mobile-editor-options");
|
||||
document.addEventListener("pointerdown", event => {
|
||||
if (mobileEditorOptions?.open && !event.target.closest("#mobile-editor-options")) {
|
||||
mobileEditorOptions.open = false;
|
||||
}
|
||||
}, { passive: true });
|
||||
document.addEventListener("keydown", event => {
|
||||
if (event.key === "Escape" && mobileEditorOptions?.open) mobileEditorOptions.open = false;
|
||||
});
|
||||
modeToggle.addEventListener("click", () => { uiState = { ...uiState, mode: uiState.mode === "markdown" ? "text" : "markdown" }; applyUi({ write: true }); });
|
||||
lineToggle.addEventListener("change", () => { localStorage.setItem(notePreferenceKey("line-numbers"), lineToggle.checked ? "on" : "off"); syncMobileEditorControls(); renderGutter(); scheduleEditorSettingsSave({ personal: true }); });
|
||||
previewLineToggle.addEventListener("change", () => { localStorage.setItem(notePreferenceKey("preview-line-numbers"), previewLineToggle.checked ? "on" : "off"); syncMobileEditorControls(); renderGutter(); alignPreviewLineNumbers(preview); scheduleEditorSettingsSave({ personal: true }); });
|
||||
|
||||
Reference in New Issue
Block a user