自动为 linux.do 论坛帖子生成目录 (TOC) - 适用于未启用自动目录的帖子
// ==UserScript==
// @name Linux.do Auto TOC
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 自动为 linux.do 论坛帖子生成目录 (TOC) - 适用于未启用自动目录的帖子
// @author marhoosh
// @match https://linux.do/t/topic/*
// @grant GM_registerMenuCommand
// @grant unsafeWindow
// @license MIT
// @icon https://linux.do/uploads/default/optimized/4X/6/a/6/6a6affc7b1ce8140279e959d32671304db06d5ab_2_180x180.png
// ==/UserScript==
(function () {
'use strict';
function forceGenerateTOC() {
// 如果当前 URL 不是帖子页面,直接退出
if (!location.pathname.includes('/t/topic')) {
return;
}
const container = Discourse.__container__;
// 获取必要的服务和控制器
const tocProcessor = container.lookup("service:toc-processor");
const topicController = container.lookup("controller:topic");
const topic = topicController.get("model");
if (tocProcessor.hasTOC || topic.currentPost > 1) {
return;
}
try {
// 访问页面原生的 Discourse 对象
const win = unsafeWindow || window;
const Discourse = win.Discourse;
if (!Discourse || !Discourse.__container__) {
console.error("未检测到 Discourse 容器,该脚本仅适用于 Discourse 论坛。");
return;
}
if (!tocProcessor) {
console.error("当前站点未安装或未启用 DiscoTOC 插件,无法生成目录。");
return;
}
if (!topicController || !topicController.get("model")) {
console.warn("未找到话题信息,请确保你在具体的帖子页面使用此功能。");
return;
}
// 尝试获取主贴(通常是 1 楼)
// 注意:如果页面未加载主贴(例如直接跳转到楼层深处),可能需要从 postStream 中查找
let post = topic.get("postStream.posts").find(p => p.post_number === 1);
if (!post) {
// 如果当前未加载主贴,尝试提示用户
console.warn("无法找到主贴数据。请尝试滚动到页面顶部,让主贴加载出来后再重试。");
return;
}
// console.log("Discourse Force TOC: 找到主贴 ID:", post.id);
// 核心操作 1: 为了让状态持久化(防止组件刷新后丢失),我们“欺骗”插件说这个帖子有标记
// 检查 content 中是否已有标记,没有则添加
if (!post.cooked.includes('data-theme-toc="true"')) {
// 修改内存中的帖子内容(不会影响服务器)
// 尝试兼容 Ember Object 的 set 方法和普通对象赋值
if (typeof post.set === 'function') {
post.set('cooked', post.cooked + '\n<div data-theme-toc="true"></div>');
} else {
post.cooked += '\n<div data-theme-toc="true"></div>';
}
}
// 核心操作 2: 强制调用处理逻辑
// 直接调用 processPostContent 是最稳妥的,它绕过了 checkPostforTOC 中的一些条件判断(如 autoTOC 设置)
// console.log("Discourse Force TOC: 强制处理帖子内容...");
tocProcessor.processPostContent(post.cooked, post.id);
// 检查结果
if (tocProcessor.hasTOC) {
// 强制设置为可见
if (typeof tocProcessor.set === 'function') {
tocProcessor.set('isTocVisible', true);
} else {
tocProcessor.isTocVisible = true;
}
// 尝试通知 UI 更新(有些情况下可能需要手动触发)
// 通常修改 tracked 属性就足够了
console.log("Discourse Force TOC: ✅ 目录生成成功");
// 使用非阻塞通知,或者简单的 console.error
// 考虑到用户体验,这里用简单的 console.error,用户确定后可以看到效果
// 如果不想弹窗,可以注释掉下面这行
// console.error("目录已生成!\n如果未出现,请检查帖子标题是否过少(默认需要至少3个标题)。");
} else {
console.warn("操作已执行,但目录未生成。\n\n可能原因:\n1. 帖子中的标题(H1-H5)少于插件阈值(通常默认为 3 个)。\n2. 帖子内容格式无法被解析。");
}
} catch (e) {
console.error("Discourse Force TOC Error:", e);
}
}
// 注册脚本菜单
GM_registerMenuCommand("强制生成本页目录", forceGenerateTOC);
setInterval(forceGenerateTOC, 2000);
})();