// ==UserScript==
// @name The gnu mailing list archive thread shows optimization
// @namespace http://tampermonkey.net/
// @version 1.0
// @match https://lists.gnu.org/archive/html/*/*/threads.html
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @require https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(`
:root {
--bg-color: white;
--text-color: #333;
--meta-color: #666;
--border-color: #e0e0e0;
--card-bg: white;
--body-bg: #f8f9fa;
--link-color: #dc3545;
--child-border: #eee;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1e1e1e;
--text-color: #e0e0e0;
--meta-color: #a0a0a0;
--border-color: #444;
--card-bg: #2d2d2d;
--body-bg: #252525;
--link-color: #ff6b6b;
--child-border: #444;
}
}
body {
background-color: var(--bg-color);
color: var(--text-color);
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.thread-card {
margin: 12px 0;
padding: 15px;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--card-bg);
}
.thread-header {
cursor: pointer;
display: grid;
grid-template-columns: 1fr auto;
gap: 10px;
align-items: center;
}
.thread-meta {
color: var(--meta-color);
font-size: 0.85em;
margin-top: 4px;
}
.email-body {
margin: 10px 0;
padding: 10px;
background: var(--body-bg);
border-radius: 4px;
white-space: pre-wrap;
color: var(--text-color);
}
.original-link {
color: var(--link-color) !important;
font-family: monospace;
white-space: nowrap;
text-decoration: none !important;
border-bottom: none !important;
}
.child-threads {
margin-left: 28px;
border-left: 2px solid var(--child-border);
}
`);
const extractThreadInfo = ($li) => {
const authorElement = $li.children('i').first();
const dateElement = $li.children('tt').first();
// 修复点:使用直接子元素选择器
const $subjectLink = $li.children('b').first().find('a').first();
return {
subject: $subjectLink.text().trim(), // 仅获取直接子元素的标题
author: authorElement.length ?
authorElement.text().trim().replace(/[,$]/g, '') : '未知作者',
date: dateElement.length ?
dateElement.text().trim().replace(/\//g, '-') : '未知日期',
href: $subjectLink.attr('href')
};
};
async function loadEmailContent(url) {
try {
const response = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: resolve,
onerror: reject
});
});
const parser = new DOMParser();
const doc = parser.parseFromString(response.responseText, "text/html");
const headers = [
'From', 'Subject', 'Date', 'User-agent'
].reduce((acc, key) => {
const $row = $(doc).find(`tr:contains('${key}:')`);
acc[key.toLowerCase()] = $row.length ? $row.find('td').eq(1).text().trim() : 'N/A';
return acc;
}, {});
const body = response.responseText
.split('<!--X-Body-of-Message-->')[1]
?.split('<!--X-Body-of-Message-End-->')[0]
?.trim() || '内容不可用';
return {
...headers,
body: body.replace(/(\) )(.{80})/g, '$1\n$2'),
url: url
};
} catch(e) {
console.error('邮件加载失败:', e);
return null;
}
}
function createThreadElement(liNode) {
const $li = $(liNode);
const info = extractThreadInfo($li);
const $card = $('<div>').addClass('thread-card');
let isLoaded = false;
const $header = $(`
<div class="thread-header">
<div>
<div>${info.subject}</div>
<div class="thread-meta">
${info.author} • ${info.date}
</div>
</div>
<a href="${info.href}" class="original-link" target="_blank">🔗</a>
</div>
`);
const $content = $('<div>').hide();
// 定义展开方法
const expand = async () => {
if (!isLoaded) {
const data = await loadEmailContent(info.href);
if (data) {
$content.html(`
<div class="email-header">
<div><b>From:</b> ${data.from}</div>
<div><b>Date:</b> ${data.date}</div>
<div><b>Subject:</b> ${data.subject}</div>
</div>
<pre class="email-body">${data.body}</pre>
`);
isLoaded = true;
}
}
$content.slideDown();
};
// 将展开方法附加到卡片元素
$card.data('expand', expand);
$header.on('click', async (e) => {
if ($(e.target).is('a')) return;
if (!isLoaded) {
const data = await loadEmailContent(info.href);
if (data) {
$content.html(`
<div class="email-header">
<div><b>From:</b> ${data.from}</div>
<div><b>Date:</b> ${data.date}</div>
<div><b>Subject:</b> ${data.subject}</div>
</div>
<pre class="email-body">${data.body}</pre>
`);
isLoaded = true;
}
}
$content.slideToggle(async () => {
// 展开后自动展开直接子线程
if ($content.is(':visible')) {
const $childCards = $card.find('.child-threads > .thread-card');
$childCards.each(function() {
const expandFn = $(this).data('expand');
if (typeof expandFn === 'function') {
expandFn();
}
});
}
});
});
// 处理子线程
const $children = $li.children('ul').first().children('li');
if ($children.length > 0) {
const $childContainer = $('<div class="child-threads">');
$children.each(function() {
$childContainer.append(createThreadElement(this));
});
$card.append($childContainer);
}
return $card.prepend($header, $content);
}
// 应用转换
$(() => {
const $originalList = $('body > ul').first();
const $newView = $('<div id="reddit-view">');
$originalList.children('li').each(function() {
$newView.append(createThreadElement(this));
});
$('body').empty().css({
'max-width': '800px',
'margin': '0 auto',
'padding': '20px'
}).append($newView);
});
})();
10 个赞