v0.2.78
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@
|
||||
* See LICENSE file in repository root for details.
|
||||
*/
|
||||
|
||||
import { EMOJI_GROUPS } from "@rustpad/emoji-data";
|
||||
import { EMOJI_GROUPS } from "@rustpad/emoji-groups";
|
||||
import { t } from "@rustpad/i18n";
|
||||
|
||||
const RECENTS_KEY = "rustpad:recent-emojis";
|
||||
@@ -153,4 +153,9 @@ export function bindEmojiPicker({ editor, details, search, categories, grid, emp
|
||||
document.addEventListener("rustpad:languagechange", () => {
|
||||
if (details.open) render();
|
||||
});
|
||||
|
||||
if (details.open) {
|
||||
render();
|
||||
requestAnimationFrame(() => search.focus());
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@
|
||||
* See LICENSE file in repository root for details.
|
||||
*/
|
||||
|
||||
import { EMOJI_SHORTCODES } from "@rustpad/emoji-data";
|
||||
import { EMOJI_SHORTCODES } from "@rustpad/emoji-shortcodes";
|
||||
import { t } from "@rustpad/i18n";
|
||||
import { parseImageAlias } from "@rustpad/image-alias";
|
||||
|
||||
|
||||
+49
-14
@@ -17,10 +17,8 @@ import { CollaborationRevisionGapError, CollaborationSession } from "@rustpad/co
|
||||
import { copyText } from "@rustpad/clipboard";
|
||||
import { lineFromHash, lineLink, lineStartOffset } from "@rustpad/line-links";
|
||||
import { applyFormat, bindFormatShortcuts, bindIndentationShortcuts } from "@rustpad/editor-format";
|
||||
import { bindEmojiPicker } from "@rustpad/emoji-picker";
|
||||
import { updateImageAliasInLineBySource } from "@rustpad/image-alias";
|
||||
import { previewEditingHost } from "@rustpad/preview-edit";
|
||||
import { bindPdfExport } from "@rustpad/pdf-export";
|
||||
import { createRenderQueue } from "@rustpad/render-queue";
|
||||
import { alignPreviewLineNumbers, renderMarkdown, setMarkdownFiles, unresolvedMarkdownFileAliases } from "@rustpad/markdown";
|
||||
import { getNickname, getGuestId, getAuthToken, getAccessToken, setAccessToken } from "@rustpad/session";
|
||||
@@ -270,12 +268,7 @@ export function startNoteEditor(adapter) {
|
||||
documentTitle.textContent = info.title;
|
||||
}
|
||||
globalColor = info.global_color || ""; noteColor = info.note_color || "";
|
||||
if (getAuthToken()) {
|
||||
const colors = await adapter.loadColor(accountHeaders());
|
||||
globalColor = colors.global_color || ""; noteColor = colors.note_color || "";
|
||||
} else {
|
||||
noteColor = readGuestColor();
|
||||
}
|
||||
if (!getAuthToken()) noteColor = readGuestColor();
|
||||
updateMarkdownFiles(info.files || []);
|
||||
if (info.personal_editor_settings) {
|
||||
compactToggle.checked = info.compact_view !== false;
|
||||
@@ -1633,7 +1626,7 @@ export function startNoteEditor(adapter) {
|
||||
});
|
||||
socket.connect();
|
||||
}
|
||||
bindIdentityDialog({ dialog: identityDialog, onIdentity: async (value, session) => { nickname = value; accountSession = session || await validateCurrentSession(); accessToken = shareToken || getAccessToken(adapter.access.kind, adapter.access.key); identityDialog.close(); updateCurrentUser(); await loadNoteInfo(); await syncFavoriteButton(); if (info.protected && info.access_level === "none") passwordDialog.showModal(); else { loadFiles(); connect(); } } });
|
||||
bindIdentityDialog({ dialog: identityDialog, onIdentity: async (value, session) => { nickname = value; accountSession = session || await validateCurrentSession(); accessToken = shareToken || getAccessToken(adapter.access.kind, adapter.access.key); identityDialog.close(); updateCurrentUser(); await loadNoteInfo(); if (info.protected && info.access_level === "none") passwordDialog.showModal(); else { loadFiles(); connect(); } } });
|
||||
identityDialog.addEventListener("close", () => { if (!nickname) queueMicrotask(() => { if (!identityDialog.open) identityDialog.showModal(); }); });
|
||||
async function showSystemNotFound() {
|
||||
try {
|
||||
@@ -1667,7 +1660,6 @@ export function startNoteEditor(adapter) {
|
||||
adapter.configureView?.(info);
|
||||
applyUi({ write: true, replace: true });
|
||||
updateCurrentUser();
|
||||
await syncFavoriteButton();
|
||||
if (info.protected && info.access_level === "none") passwordDialog.showModal();
|
||||
else { loadFiles(); connect(); }
|
||||
} catch (e) {
|
||||
@@ -2074,14 +2066,57 @@ export function startNoteEditor(adapter) {
|
||||
});
|
||||
bindFormatShortcuts(editor);
|
||||
bindIndentationShortcuts(editor, { size: 2 });
|
||||
bindPdfExport({
|
||||
const pdfExportArgs = {
|
||||
button: document.querySelector("#export-pdf"),
|
||||
dialog: document.querySelector("#pdf-export-dialog"),
|
||||
form: document.querySelector("#pdf-export-form"),
|
||||
preview,
|
||||
title: () => document.querySelector("#document-title")?.textContent?.trim() || document.title,
|
||||
};
|
||||
let pdfExportBound = false;
|
||||
let pdfExportLoad = null;
|
||||
pdfExportArgs.button?.addEventListener("click", async event => {
|
||||
if (pdfExportBound) return;
|
||||
event.stopImmediatePropagation();
|
||||
pdfExportLoad ||= import("@rustpad/pdf-export").then(({ bindPdfExport }) => {
|
||||
bindPdfExport(pdfExportArgs);
|
||||
pdfExportBound = true;
|
||||
});
|
||||
try {
|
||||
await pdfExportLoad;
|
||||
if (!pdfExportArgs.dialog.open) pdfExportArgs.dialog.showModal();
|
||||
} catch (error) {
|
||||
pdfExportLoad = null;
|
||||
console.error("Failed to load PDF export", error);
|
||||
toast.danger(t("editor.pdfExportFailed", {}, "Could not prepare the PDF."), {
|
||||
title: t("editor.exportPdf", {}, "Export PDF"),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const emojiPickerArgs = {
|
||||
editor,
|
||||
details: document.querySelector("#emoji-picker"),
|
||||
search: document.querySelector("#emoji-search"),
|
||||
categories: document.querySelector("#emoji-categories"),
|
||||
grid: document.querySelector("#emoji-grid"),
|
||||
empty: document.querySelector("#emoji-empty"),
|
||||
};
|
||||
let emojiPickerLoad = null;
|
||||
const loadEmojiPicker = async () => {
|
||||
if (emojiPickerLoad) return emojiPickerLoad;
|
||||
emojiPickerLoad = import("@rustpad/emoji-picker").then(({ bindEmojiPicker }) => {
|
||||
bindEmojiPicker(emojiPickerArgs);
|
||||
}).catch(error => {
|
||||
emojiPickerLoad = null;
|
||||
throw error;
|
||||
});
|
||||
return emojiPickerLoad;
|
||||
};
|
||||
emojiPickerArgs.details?.addEventListener("toggle", () => {
|
||||
if (!emojiPickerArgs.details.open) return;
|
||||
void loadEmojiPicker().catch(error => console.error("Failed to load emoji picker", error));
|
||||
});
|
||||
bindEmojiPicker({ editor, details: document.querySelector("#emoji-picker"), search: document.querySelector("#emoji-search"), categories: document.querySelector("#emoji-categories"), grid: document.querySelector("#emoji-grid"), empty: document.querySelector("#emoji-empty") });
|
||||
document.querySelector("#shortcuts-button").addEventListener("click", () => document.querySelector("#shortcuts-dialog").showModal());
|
||||
document.querySelector("#close-shortcuts").addEventListener("click", () => document.querySelector("#shortcuts-dialog").close());
|
||||
|
||||
@@ -2259,7 +2294,7 @@ export function startNoteEditor(adapter) {
|
||||
password = "";
|
||||
setPagePasswordInput.value = "";
|
||||
await loadNoteInfo();
|
||||
await syncFavoriteButton();
|
||||
void syncFavoriteButton();
|
||||
resourceUnlocked = false;
|
||||
socket?.stop();
|
||||
loadFiles();
|
||||
@@ -2357,7 +2392,7 @@ export function startNoteEditor(adapter) {
|
||||
document.querySelector("#open-password")?.focus();
|
||||
}
|
||||
});
|
||||
document.querySelector("#password-form").addEventListener("submit", async e => { e.preventDefault(); try { password = document.querySelector("#open-password").value; const result = await adapter.requestAccess(password); setAccessToken(adapter.access.kind, adapter.access.key, result.granted); accessToken = getAccessToken(adapter.access.kind, adapter.access.key); password = ""; document.querySelector("#open-password").value = ""; document.querySelector("#password-error").textContent = ""; await loadNoteInfo(); await syncFavoriteButton(); loadFiles(); connect(); toast.success("Editing access has been unlocked.", { title: "Note unlocked" }); } catch (error) { document.querySelector("#password-error").textContent = error.message; toast.danger(error.message, { title: "Could not unlock note" }); } });
|
||||
document.querySelector("#password-form").addEventListener("submit", async e => { e.preventDefault(); try { password = document.querySelector("#open-password").value; const result = await adapter.requestAccess(password); setAccessToken(adapter.access.kind, adapter.access.key, result.granted); accessToken = getAccessToken(adapter.access.kind, adapter.access.key); password = ""; document.querySelector("#open-password").value = ""; document.querySelector("#password-error").textContent = ""; await loadNoteInfo(); void syncFavoriteButton(); loadFiles(); connect(); toast.success("Editing access has been unlocked.", { title: "Note unlocked" }); } catch (error) { document.querySelector("#password-error").textContent = error.message; toast.danger(error.message, { title: "Could not unlock note" }); } });
|
||||
const historyPanel = document.querySelector("#history-panel"); document.querySelector("#history-button").addEventListener("click", async () => { if (info?.protected && !resourceUnlocked) { if (!passwordDialog.open) passwordDialog.showModal(); document.querySelector("#open-password")?.focus(); return; } historyPanel.classList.add("open"); historyPanel.setAttribute("aria-hidden", "false"); document.body.classList.add("history-open"); const list = document.querySelector("#history-list"); list.innerHTML = '<p class="empty">Loading…</p>'; try { const revisions = await adapter.loadHistory(accessToken); list.innerHTML = revisions.length ? revisions.map((r, i) => { const snippet = escapeHtml(r.content.trim().split("\n").slice(0, 3).join(" · ").slice(0, 150) || "Empty note"); const author = r.author || "Unknown author"; return `<article class="revision"><span class="revision__marker" style="--owner:${colorFor(author)}"></span><div><div class="revision__meta"><strong>${escapeHtml(author)}</strong><time>${formatDate(r.created_at)}</time></div><p class="revision__snippet">${snippet}</p><button data-preview="${r.id}">Preview</button><button data-revision="${r.id}">Restore</button><div class="revision__preview" id="preview-${r.id}" hidden></div></div></article>`; }).join("") : '<p class="empty">No history yet.</p>'; for (const r of revisions) { list.querySelector(`[data-preview="${r.id}"]`)?.addEventListener("click", () => { const el = list.querySelector(`#preview-${r.id}`); el.hidden = !el.hidden; el.textContent = r.content; }); list.querySelector(`[data-revision="${r.id}"]`)?.addEventListener("click", async () => { await adapter.restoreRevision(r.id, accessToken); toast.success("The selected revision is now the current version.", { title: "Version restored" }); }); } } catch (e) { list.innerHTML = `<p class="error">${escapeHtml(e.message)}</p>`; toast.danger(e.message, { title: "Could not load version history" }); } }); document.querySelector("#close-history").addEventListener("click", () => { historyPanel.classList.remove("open"); historyPanel.setAttribute("aria-hidden", "true"); document.body.classList.remove("history-open"); });
|
||||
|
||||
const deleteNoteButton = document.querySelector("#delete-note"); if (deleteNoteButton && adapter.deleteNote) deleteNoteButton.addEventListener("click", async () => { try { await adapter.deleteNote(info, accessToken); } catch (error) { toast.danger(error.message, { title: "Could not delete note" }); } });
|
||||
|
||||
Reference in New Issue
Block a user