fix: update regex for blob URL matching and enhance display name input in comments

This commit is contained in:
2026-05-12 22:26:37 +10:00
parent b09a329d62
commit a4926c1368
5 changed files with 79 additions and 37 deletions

Binary file not shown.

View File

@@ -780,7 +780,7 @@
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
async function uploadRemainingBlobs(markdown) { async function uploadRemainingBlobs(markdown) {
const blobMatches = [...markdown.matchAll(/!\[([^\]]*)\]\((blob:[^)]+)\)/g)]; const blobMatches = [...markdown.matchAll(/!\[([^\]]*)\]\((blob:[^\s)]+)/g)];
if (blobMatches.length === 0) return markdown; if (blobMatches.length === 0) return markdown;
console.warn(`[Blog Editor] Found ${blobMatches.length} remaining blob URLs - uploading them`); console.warn(`[Blog Editor] Found ${blobMatches.length} remaining blob URLs - uploading them`);
const uploads = blobMatches.map(async m => { const uploads = blobMatches.map(async m => {
@@ -820,7 +820,8 @@
let result = markdown; let result = markdown;
for (const { blobUrl, relPath } of results) { for (const { blobUrl, relPath } of results) {
if (relPath) { if (relPath) {
result = result.replace(new RegExp(`!\\[([^\\]]*)\\]\\(${blobUrl.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\)`, 'g'), `![$1](${relPath})`); result = result.replace(new RegExp(`!\\[([^\\]]*)\\]\\(${blobUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(\\s+["'][\\s\\S]*?["'])?\\)`, 'g'), `![$1](${relPath}$2)`);
} }
} }
return result; return result;
@@ -1574,7 +1575,7 @@
const rawDoc = buildDocument(); const rawDoc = buildDocument();
console.log('[Blog Editor] savePost called, cleaning image paths...'); console.log('[Blog Editor] savePost called, cleaning image paths...');
const blobs = [...rawDoc.matchAll(/!\[[^\]]*\]\((blob:[^)]+)\)/g)].map(m => m[1]); const blobs = [...rawDoc.matchAll(/!\[[^\]]*\]\((blob:[^\s)]+)/g)].map(m => m[1]);
const unknown = [...new Set(blobs.filter(b => !blobToRelative.has(b)))]; const unknown = [...new Set(blobs.filter(b => !blobToRelative.has(b)))];
if (unknown.length > 0) { if (unknown.length > 0) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

View File

@@ -230,7 +230,7 @@ export class BlogComments {
} }
/** /**
* Renders the "Commenting as [name] ✏️" identity bar. * Renders the "Commenting as [name]" identity bar with editable text box.
* *
* @returns {void} * @returns {void}
*/ */
@@ -244,50 +244,67 @@ export class BlogComments {
label.className = "blog-comments-identity-label"; label.className = "blog-comments-identity-label";
label.textContent = "Commenting as "; label.textContent = "Commenting as ";
const nameSpan = document.createElement("strong"); const nameInput = document.createElement("input");
nameSpan.className = "blog-comments-identity-name"; nameInput.type = "text";
nameSpan.textContent = this.#displayName; nameInput.className = "blog-comments-identity-name-input";
nameInput.value = this.#displayName;
const editBtn = document.createElement("button"); nameInput.maxLength = 64;
editBtn.type = "button"; nameInput.setAttribute("aria-label", "Display name");
editBtn.className = "blog-comments-identity-edit-btn";
editBtn.setAttribute("aria-label", "Edit display name");
const remaining = this.#nickCooldownRemaining(); const remaining = this.#nickCooldownRemaining();
if (remaining > 0) { if (remaining > 0) {
editBtn.disabled = true; nameInput.disabled = true;
editBtn.title = `Wait ${remaining}s before changing name again`; nameInput.title = `Wait ${remaining}s before changing name again`;
editBtn.textContent = `✏️ ${remaining}s`; this.#startCooldownTick(nameInput);
this.#startCooldownTick(editBtn);
} else { } else {
editBtn.title = "Edit display name"; nameInput.title = "Edit your display name";
editBtn.textContent = "✏️";
editBtn.addEventListener("click", () => this.#startEditName());
} }
this.#identityEl.append(label, nameSpan, editBtn); const updateWidth = () => {
const tempSpan = document.createElement("span");
tempSpan.style.visibility = "hidden";
tempSpan.style.position = "absolute";
tempSpan.style.fontSize = "0.85rem";
tempSpan.style.fontFamily = getComputedStyle(nameInput).fontFamily;
tempSpan.textContent = nameInput.value || nameInput.placeholder || "Guest";
document.body.appendChild(tempSpan);
const width = Math.min(250, Math.max(60, tempSpan.offsetWidth + 20));
document.body.removeChild(tempSpan);
nameInput.style.width = `${width}px`;
};
nameInput.addEventListener("input", updateWidth);
nameInput.addEventListener("blur", () => {
const newName = nameInput.value.trim();
if (newName && newName !== this.#displayName) {
this.#saveDisplayName(newName);
} else {
nameInput.value = this.#displayName;
}
updateWidth();
});
this.#identityEl.append(label, nameInput);
updateWidth();
} }
/** /**
* Ticks the countdown text on the edit button without rebuilding the DOM. * Ticks the countdown on the name input without rebuilding the DOM.
* *
* @param {HTMLButtonElement} editBtn * @param {HTMLInputElement} nameInput
* @returns {void} * @returns {void}
*/ */
#startCooldownTick(editBtn) { #startCooldownTick(nameInput) {
if (this.#nickCooldownTimer !== null) clearInterval(this.#nickCooldownTimer); if (this.#nickCooldownTimer !== null) clearInterval(this.#nickCooldownTimer);
this.#nickCooldownTimer = setInterval(() => { this.#nickCooldownTimer = setInterval(() => {
const remaining = this.#nickCooldownRemaining(); const remaining = this.#nickCooldownRemaining();
if (remaining <= 0) { if (remaining <= 0) {
clearInterval(this.#nickCooldownTimer); clearInterval(this.#nickCooldownTimer);
this.#nickCooldownTimer = null; this.#nickCooldownTimer = null;
editBtn.disabled = false; nameInput.disabled = false;
editBtn.title = "Edit display name"; nameInput.title = "Edit your display name";
editBtn.textContent = "✏️";
editBtn.addEventListener("click", () => this.#startEditName());
} else { } else {
editBtn.textContent = `✏️ ${remaining}s`; nameInput.title = `Wait ${remaining}s before changing name again`;
editBtn.title = `Wait ${remaining}s before changing name again`;
} }
}, 1000); }, 1000);
} }

View File

@@ -477,7 +477,7 @@ svg.world-image {
width: 100%; width: 100%;
font-family: inherit; font-family: inherit;
font-size: 10px; font-size: 10px;
padding: 3px 8px; padding: 0 4px;
background: rgba(137, 180, 250, 0.15); background: rgba(137, 180, 250, 0.15);
color: rgba(137, 180, 250, 0.9); color: rgba(137, 180, 250, 0.9);
border: 1px solid rgba(137, 180, 250, 0.3); border: 1px solid rgba(137, 180, 250, 0.3);
@@ -778,7 +778,7 @@ body.blog-page {
.blog-post-peek-wrapper { .blog-post-peek-wrapper {
position: absolute; position: absolute;
top: 0; top: 0;
width: 160px; min-width: 60px;`n max-width: 250px;
height: 0; height: 0;
pointer-events: none; pointer-events: none;
overflow: visible; overflow: visible;
@@ -793,7 +793,7 @@ body.blog-page {
} }
.blog-post-peek { .blog-post-peek {
width: 160px; min-width: 60px;`n max-width: 250px;
background: var(--node-bg); background: var(--node-bg);
border: 1px solid var(--node-border); border: 1px solid var(--node-border);
border-radius: 8px; border-radius: 8px;
@@ -981,7 +981,7 @@ body.blog-page {
border: 1px solid rgba(137, 180, 250, 0.35); border: 1px solid rgba(137, 180, 250, 0.35);
background: rgba(137, 180, 250, 0.12); background: rgba(137, 180, 250, 0.12);
color: var(--ctp-sky); color: var(--ctp-sky);
padding: 3px 8px; padding: 0 4px;
border-radius: 999px; border-radius: 999px;
font-size: 0.78rem; font-size: 0.78rem;
} }
@@ -1576,9 +1576,9 @@ body.blog-page {
background: rgba(24, 24, 37, 0.85); background: rgba(24, 24, 37, 0.85);
border-radius: 6px; border-radius: 6px;
color: var(--ctp-text); color: var(--ctp-text);
padding: 3px 8px; padding: 0 4px;
font-size: 0.85rem; font-size: 0.85rem;
width: 160px; min-width: 60px;`n max-width: 250px;
} }
.blog-comments-identity-input:focus { .blog-comments-identity-input:focus {
@@ -1586,6 +1586,30 @@ body.blog-page {
border-color: var(--ctp-blue); border-color: var(--ctp-blue);
} }
.blog-comments-identity-name-input {
border: 1px solid rgba(205, 214, 244, 0.25);
background: rgba(24, 24, 37, 0.85);
border-radius: 6px;
color: var(--ctp-text);
padding: 3px 8px;
font-size: 0.85rem;
min-width: 60px;
max-width: 250px;
vertical-align: baseline;
transition: border-color 0.15s, background 0.15s, width 0.15s;
}
.blog-comments-identity-name-input:focus {
outline: none;
border-color: var(--ctp-blue);
background: rgba(69, 71, 90, 0.6);
}
.blog-comments-identity-name-input:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.blog-comments-identity-save-btn { .blog-comments-identity-save-btn {
background: rgba(137, 180, 250, 0.2); background: rgba(137, 180, 250, 0.2);
border: 1px solid rgba(137, 180, 250, 0.3); border: 1px solid rgba(137, 180, 250, 0.3);
@@ -3067,7 +3091,7 @@ body.blog-page {
color: rgba(243, 139, 168, 0.9); color: rgba(243, 139, 168, 0.9);
border: 1px solid rgba(243, 139, 168, 0.3); border: 1px solid rgba(243, 139, 168, 0.3);
border-radius: 4px; border-radius: 4px;
padding: 3px 8px; padding: 0 4px;
font-size: 10px; font-size: 10px;
cursor: pointer; cursor: pointer;
transition: all 0.15s; transition: all 0.15s;
@@ -3124,7 +3148,7 @@ body.blog-page {
font-weight: 500; font-weight: 500;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
padding: 3px 8px; padding: 0 4px;
border-radius: 3px; border-radius: 3px;
} }