Linux.do Auto TOC

自动为 linux.do 论坛帖子生成目录 (TOC) - 适用于未启用自动目录的帖子

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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);


})();