This commit is contained in:
Mateusz Gruszczyński committed 2026-10-18 14:45:59 +02:00
1 parent 5cb6960b04
commit 0d7ffea17a
13 files changed
+17 -135

No files matched your search

+1 -3
View File
@@ -253,8 +253,6 @@
</form>
</dialog>
<script>
// If initial account scripts/network never finish, offer a local retry rather
// than leaving the footer placeholder indefinitely (without blocking home).
setTimeout(() => {
const slot = document.getElementById("footer-account-slot");
if (slot?.dataset.state !== "loading") return;
@@ -262,7 +260,7 @@
slot.setAttribute("aria-busy", "false");
const retry = document.getElementById("footer-account-retry");
if (retry) retry.hidden = false;
}, 15000);
}, 10000);
</script>
<div id="toast" class="toast-region" aria-live="polite" aria-label="Notifications" aria-relevant="additions removals">
</div>
-3
View File
@@ -4,9 +4,6 @@
* See LICENSE.md for terms.
*/
// Preserve existing nodes. Remove stale entries first: otherwise deleting a
// user near the start of a 1000-person list would move every following node.
// `desired` must contain each node at most once.
export function reconcileChildren(container, desired) {
const retained = new Set(desired);
for (const node of [...container.childNodes]) {
-2
View File
@@ -7,7 +7,5 @@
* See LICENSE file in repository root for details.
*/
// Generated from Unicode Emoji 17.0 emoji-test.txt.
// Native glyphs use the platform emoji font (Apple style on Apple devices).
export { EMOJI_GROUPS } from "@rustpad/emoji-groups";
export { EMOJI_SHORTCODES } from "@rustpad/emoji-shortcodes";
-6
View File
@@ -13,8 +13,6 @@ const MAX_RESULTS = 8;
const MENTION_SUFFIX = /[\p{L}\p{M}\p{N}_.+-]{0,64}$/u;
const MENTION_BOUNDARY = /[\p{L}\p{N}_@]/u;
// A suggestion is offered only for an @word being typed at a collapsed caret.
// Do not interpret addresses such as john@example.org as mentions.
export function mentionAtCaret(value, caret) {
if (caret < 1 || caret > value.length) return null;
const lineStart = value.lastIndexOf("\n", caret - 1) + 1;
@@ -69,8 +67,6 @@ export function createMentionAutocomplete(editor) {
menu.hidden = true;
shell.append(menu);
// A single-line offscreen mirror measures the caret's horizontal position,
// including proportional fonts and tabs, without relying on text estimates.
const mirror = document.createElement("div");
mirror.className = "mention-caret-mirror";
mirror.setAttribute("aria-hidden", "true");
@@ -158,7 +154,6 @@ export function createMentionAutocomplete(editor) {
editor.focus();
const inserted = `@${user.nickname}${separator}`;
editor.setRangeText(inserted, start, end, "end");
// Use the normal input pipeline: live collaboration, authorship and undo/redo.
editor.dispatchEvent(new InputEvent("input", { bubbles: true, inputType: "insertReplacementText", data: inserted }));
}
@@ -189,7 +184,6 @@ export function createMentionAutocomplete(editor) {
const label = document.createElement("span");
label.textContent = `@${user.nickname}`;
button.append(dot, label);
// Keep textarea selection intact while clicking with a mouse.
button.addEventListener("mousedown", event => event.preventDefault());
button.addEventListener("click", () => accept(index));
menu.append(button);
-8
View File
@@ -11,10 +11,6 @@ import { t } from "@rustpad/i18n";
import { normalizeMentionUsers } from "@rustpad/mention-autocomplete";
function escapeRegex(value) { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }
// Find real @mentions in the source, not arbitrary text that resembles an email
// address or an @username inside a code fence, inline code, link or URL.
// Offsets use UTF-16 code units, just like textarea.setSelectionRange().
export function collectMentionOccurrences(source, authorizedUsers, { markdown = true } = {}) {
const members = normalizeMentionUsers(authorizedUsers);
if (!members.length) return [];
@@ -39,7 +35,6 @@ export function collectMentionOccurrences(source, authorizedUsers, { markdown =
const excluded = [];
if (markdown) {
// The Markdown renderer stashes these constructs before highlighting mentions.
const hidden = [
/`[^`]+`/g,
/!?\[[^\]]*\]\([^\s)]+(?:\s+["'][^"']*["'])?\)/g,
@@ -90,8 +85,6 @@ export function createMentionNavigator({ container, list, previewContainer, prev
if (!entries.has(mention.key)) entries.set(mention.key, []);
entries.get(mention.key).push(mention);
}
// Keep a reference to each visible mention, so the jump can target the
// exact chip even if several mentions are on the same source line.
if (markdown) {
const visible = new Map();
for (const node of preview.querySelectorAll(".markdown-mention")) {
@@ -174,7 +167,6 @@ export function createMentionNavigator({ container, list, previewContainer, prev
}
if (view !== "edit") {
navigatePreview(mention);
// Split view scroll synchronization may run after the textarea scroll event.
if (view === "split") requestAnimationFrame(() => {
if (lastJump.get(key) === mention.start) navigatePreview(mention);
});
+2 -3
View File
@@ -62,7 +62,7 @@ export function startNoteEditor(adapter) {
if (!bootLoading || bootFinishing) return;
bootFinishing = true;
bootLoading.complete("access");
bootLoading.skip("files"); // Identity/password dialog is ready to use.
bootLoading.skip("files");
bootLoading.ready();
}
let accessToken = shareToken || getAccessToken(adapter.access.kind, adapter.access.key), password = "", nickname = getNickname(), info, socket, saveTimer, applyingRemote = false, applyingHistory = false, resourceUnlocked = false, uiState = readEditorState(), authorship = parseAuthorship("", "[]"), previousContent = "", globalColor = "", noteColor = "", presenceUsers = [], authorshipMode = "simple", authorshipColorsEnabled = true, lastRevealedLineHash = "", lastRevealedHeadingHash = "", flushRequested = false, accountSession = null;
@@ -291,8 +291,7 @@ export function startNoteEditor(adapter) {
setMarkdownMentions([]);
mentionAutocomplete.setUsers([]);
mentionNavigator.setUsers([]);
// Session validation can finish before loadNoteInfo(); avoid a premature
// request (and a second request when note info becomes available).
if (!accountSession || !info) { render(); return; }
try {
const params = new URLSearchParams({ kind: adapter.access.kind, slug: adapter.access.key });
+2 -4
View File
@@ -35,7 +35,7 @@ export function clearResourceAccessState() {
if (key?.startsWith("rustpad:access:")) localStorage.removeItem(key);
}
}
// Remove plaintext passwords saved by the previous frontend version.
for (let i = localStorage.length - 1; i >= 0; i--) {
const key = localStorage.key(i);
if (key?.startsWith("rustpad:workspace:") && key.endsWith(":password")) localStorage.removeItem(key);
@@ -43,8 +43,7 @@ for (let i = localStorage.length - 1; i >= 0; i--) {
const NICKNAME_KEY = "rustpad:nickname";
const NICKNAME_COOKIE = "rustpad_nickname";
// A session cookie is shared by tabs, but disappears when the browser session
// ends. The nickname is not an authentication secret.
function getCookie(name) {
const prefix = `${name}=`;
const item = document.cookie.split("; ").find(value => value.startsWith(prefix));
@@ -79,7 +78,6 @@ export function getGuestId() {
}
const AUTH_STATE_KEY = "rustpad:auth-state";
// Remove browser-readable account tokens created by older frontend versions.
localStorage.removeItem("rustpad:auth-token");
sessionStorage.removeItem("rustpad:auth-token");
export function getAuthToken() { return localStorage.getItem(AUTH_STATE_KEY) ? "cookie" : ""; }
+2 -1
View File
@@ -38,7 +38,8 @@
<a class="text-button" href="/">Back to home</a>
</form>
</dialog>
<div id="toast" class="toast-region" aria-live="polite" aria-label="Notifications" aria-relevant="additions removals"></div>
<div id="toast" class="toast-region" aria-live="polite" aria-label="Notifications" aria-relevant="additions removals">
</div>
</body>
</html>