added 引用评论功能增加

This commit is contained in:
Vaica committed 2026-01-24 19:34:32 +08:00
1 parent b9fd4cb7f6
commit 34108e71d0
2 files changed
+237

No files matched your search

+104
View File
@@ -2941,9 +2941,11 @@ img[atk-emoticon] {
/* Fix Artalk comment textarea color in light mode */
.atk-textarea {
color: #1a1a1a !important;
background: #fff !important;
}
[data-theme="dark"] .atk-textarea {
color: #e5e7eb !important;
background: transparent !important;
}
/* About Page Card Style */
@@ -2959,6 +2961,108 @@ img[atk-emoticon] {
box-shadow: none !important;
}
/* Selection Popup (Quote Button) */
#selection-popup {
position: fixed;
display: none;
background: rgba(30, 30, 30, 0.9);
color: #fff;
padding: 8px 16px;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
z-index: 2147483647;
transform: translate(-50%, -100%);
pointer-events: auto;
/* Removed backdrop-filter for performance */
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transition: opacity 0.2s, transform 0.2s;
font-weight: 500;
line-height: 1.4;
user-select: none;
-webkit-user-select: none;
align-items: center;
gap: 6px;
white-space: nowrap;
will-change: transform, opacity;
animation: popIn 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28);
}
@keyframes popIn {
0% { transform: translate(-50%, -80%) scale(0.9); opacity: 0; }
100% { transform: translate(-50%, -100%) scale(1); opacity: 1; }
}
#selection-popup i {
font-size: 16px;
}
#selection-popup::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -6px;
border-width: 6px;
border-style: solid;
border-color: rgba(30, 30, 30, 0.9) transparent transparent transparent;
}
#selection-popup:hover {
transform: translate(-50%, -110%) scale(1.05);
background: #000;
box-shadow: 0 6px 16px rgba(0,0,0,0.25);
}
[data-theme="dark"] #selection-popup {
background: rgba(255, 255, 255, 0.95);
color: #000;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
[data-theme="dark"] #selection-popup::after {
border-color: rgba(255, 255, 255, 0.95) transparent transparent transparent;
}
[data-theme="dark"] #selection-popup:hover {
background: #fff;
}
/* Artalk Comment Blockquote Redesign & Dark Mode Fix */
.atk-content blockquote {
margin: 10px 0 6px 0; /* Reduced bottom margin for tighter layout */
padding: 12px 16px;
background-color: var(--bankuai-color);
border-left: 4px solid var(--text-highlight-color);
border-radius: 4px;
color: var(--text-content-color);
position: relative;
font-style: normal;
font-weight: normal;
transition: all 0.3s ease;
}
.atk-content blockquote + p {
margin-top: 4px !important; /* Reduce spacing for paragraph immediately following blockquote */
}
.atk-content blockquote p {
margin: 0;
color: inherit !important;
font-size: 0.95rem !important;
line-height: 1.6 !important;
}
[data-theme="dark"] .atk-content blockquote {
background-color: rgba(255, 255, 255, 0.05);
border-left-color: #4b5563; /* Use a slightly lighter gray for border in dark mode */
color: var(--text-content-dark-color);
}
[data-theme="dark"] .atk-content blockquote p {
color: inherit !important;
}
/* Category Page Styles */
.category-container {
max-width: 100%;
+133
View File
@@ -366,8 +366,141 @@ function initFloatingTools() {
// Listen for PJAX complete to update visibility
document.addEventListener('pjax:complete', function() {
updateFloatingToolsVisibility();
hideSelectionPopup();
});
document.addEventListener('DOMContentLoaded', function() {
initFloatingTools();
initSelectionPopup();
});
// Selection Popup Logic
let selectionPopup = null;
let selectionTimeout;
function initSelectionPopup() {
if (document.getElementById('selection-popup')) return;
selectionPopup = document.createElement('div');
selectionPopup.id = 'selection-popup';
selectionPopup.innerHTML = '<i class="ri-chat-quote-line"></i> <span>引用评论</span>';
document.body.appendChild(selectionPopup);
// Prevent mousedown on popup from clearing selection
selectionPopup.addEventListener('mousedown', function(e) {
e.preventDefault();
e.stopPropagation();
});
// Handle click on popup
selectionPopup.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
let selection = window.getSelection().toString();
// Optimize: Remove extra whitespace and limit length
if (selection) {
// Collapse multiple empty lines into max 2 newlines, and trim each line
selection = selection.split('\n')
.map(line => line.trim())
.filter(line => line !== '')
.join('\n');
if (selection.length > 500) {
selection = selection.substring(0, 500) + '...';
}
scrollToCommentsAndQuote(selection);
hideSelectionPopup();
window.getSelection().removeAllRanges();
}
});
// Global listeners for selection
document.addEventListener('mouseup', handleSelectionChange);
document.addEventListener('keyup', handleSelectionChange);
}
function hideSelectionPopup() {
if (selectionPopup) {
selectionPopup.style.display = 'none';
}
}
function handleSelectionChange(e) {
// Debounce to avoid rapid firing
clearTimeout(selectionTimeout);
selectionTimeout = setTimeout(() => {
const selection = window.getSelection();
const text = selection.toString().trim();
const content = document.querySelector('.post-content'); // Main article container
if (!selectionPopup || !content) return;
// Check if selection is empty
if (!text || selection.rangeCount === 0) {
hideSelectionPopup();
return;
}
const range = selection.getRangeAt(0);
// Check if the common ancestor is inside .post-content
// Optimization: Check container existence before heavy contains check
if (!content.contains(range.commonAncestorContainer)) {
hideSelectionPopup();
return;
}
// Optimization: Use requestAnimationFrame for UI updates
requestAnimationFrame(() => {
// Show first to ensure measurements if needed (though we set top/left directly)
selectionPopup.style.display = 'flex';
// Position logic: Follow mouse if available (mouseup), else fallback to selection rect (keyup)
if (e && e.type === 'mouseup') {
// Optimization: Avoid getBoundingClientRect on mouseup
// Position above the mouse cursor
selectionPopup.style.top = `${e.clientY - 40}px`;
selectionPopup.style.left = `${e.clientX}px`;
} else {
// Fallback for keyboard selection (needs rect)
const rect = range.getBoundingClientRect();
// Check if visible and valid
if (rect.width === 0 || rect.height === 0) {
hideSelectionPopup();
return;
}
selectionPopup.style.top = `${rect.top}px`;
selectionPopup.style.left = `${rect.left + rect.width / 2}px`;
}
});
}, 150);
}
function scrollToCommentsAndQuote(text) {
const comments = document.getElementById('Comments');
if (comments) {
comments.scrollIntoView({ behavior: 'smooth' });
// Wait for scroll to complete (approximate)
setTimeout(() => {
// Find Artalk textarea
const textarea = document.querySelector('.atk-textarea');
if (textarea) {
const quote = `> ${text}\n\n`;
// Overwrite existing content as requested
textarea.value = quote;
textarea.focus();
// Trigger input event for Vue/React binding (Artalk uses Vue)
textarea.dispatchEvent(new Event('input', { bubbles: true }));
textarea.dispatchEvent(new Event('change', { bubbles: true }));
if (window.Qmsg) {
Qmsg.success('引用成功');
}
}
}, 500);
}
}