8 层功力的 remnote 制卡 并多多一键制卡更新,支持列表项制卡,增加一键移除

本贴最后更新于 1029 天前,其中的信息可能已经事过境迁

需要了解是什么参见并夕夕版 文档树制卡 v0.5 支持直接获取路径和分屏,演示见下:

一键添加和移除.gif

列表项制卡的符号参考了 remnote,也就是》》和 >>。要是有什么容易打出来也不大可能有其他用途、而且好看的符号也可以提,当然更欢迎自行添加。

另外,由于接口的限制,暂时没办法移除使用快捷键添加的卡片——因为获取不到 ALL 卡包。

喜欢的话留个言,也让我知道除我以外还有人用。

// 一键移除卡片 v0.1
// 请求函数
// 使用匿名函数,防止污染空间
(() => {

    function request(url, data = null, method = "POST") {
        return new Promise((resolve, reject) => {
            if (method.toUpperCase() == "POST") {
                fetch(url, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(data),
                })
                    .then(
                        (data) => resolve(data.json()),
                        (error) => {
                            reject(error);
                        }
                    )
                    .catch((err) => {
                        console.error("请求失败:", err);
                    });
            }
        });
    }

    // 弹出提示信息
    async function showMessage(msg) {
        await request("/api/notification/pushMsg", { msg, timeout: 3000 });
    }


    function getCurrentPage() {
        //获取当前屏幕
        const currentScreen = document.querySelector(".layout__wnd--active");
        //获取当前页面
        const currentPage = currentScreen.querySelector(
            ".fn__flex-1.protyle:not(.fn__none)"
        );
        return currentPage;
    }

    //传入 node节点列表 arr和数据抽取函数 fliter。
    // fliter 传入一个节点,返回一个字典
    // 字典格式:
    // {status,data}
    // status 为 ok,则代表为抽取成功,将data压入返回列表anwer,并最后返回。
    function iterArr(arr, fliter) {

        if (!arr && arr.length <= 0) {
            return [];
        }

        length = arr.length;
        anwer = [];
        for (let node of arr) {
            data = fliter(node);
            if (data["status"] === "ok" && data["data"]) {
                anwer.push(data["data"]);
            }
        }
        return anwer;
    }

    function fliterBlockCard(node) {
        return {
            status: "ok",
            data: node.getAttribute("data-node-id")
        }
    }

    async function removeCards() {
        //获取全部deck
        let res = await request("/api/riff/getRiffDecks", {});

        //获取deck.id
        let customDeckIdArr = [];
        Array.from(res.data).forEach((item) => {

            customDeckIdArr.push(item.id);
        })

        if (customDeckIdArr.length === 0) {
            showMessage(`未能获取到卡包`);
            return;
        }

        // 获取需要被制成卡片的块的ID
        const currentPage = getCurrentPage()
        //已制卡块
        const cardedBlockList = currentPage.querySelectorAll(".protyle-wysiwyg [data-node-id][custom-riff-decks]")


        let arr = [];
        var cardedBlockIdList = iterArr(cardedBlockList, fliterBlockCard)

        arr = arr.concat(cardedBlockIdList)


        if (arr.length === 0) {
            showMessage(`未能获取到页面的卡片`);
            return;
        }

        // 去重
        arr = [... new Set(arr)];

        for (deckIndex in customDeckIdArr) {

            // 把标记所在块全部制成卡片
            let body = {
                deckID: customDeckIdArr[deckIndex],
                blockIDs: arr,
            };
            let res = await request("/api/riff/removeRiffCards", body);
            if (res.code === 0) {
                showMessage(`${res.data.name}卡包的总卡片数:${res.data.size}`);
            }
        }

    }

    const barMode = document.getElementById("barMode");
    barMode.insertAdjacentHTML(
        "beforebegin",
        '<div id="removeCards" class="toolbar__item b3-tooltips b3-tooltips__se" aria-label="移除卡片" ></div>'
    );
    const removeCardsBtn = document.getElementById("removeCards");
    removeCardsBtn.style.width = "auto";
    const removeCardsBtnIcon = `<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M600 826v-60h145v60H600Zm0-368v-60h280v60H600Zm0 184v-60h235v60H600ZM125 381H80v-60h170v-45h135v45h170v60h-45v415q0 24-18 42t-42 18H185q-24 0-42-18t-18-42V381Zm60 0v415h265V381H185Zm0 0v415-415Z"/></svg>`;
    removeCardsBtn.innerHTML = removeCardsBtnIcon;
    removeCardsBtn.addEventListener(
        "click",
        (e) => {
            removeCards();
            e.stopPropagation();
            e.preventDefault();
        },
        true
    );

})();
// 一键制卡 v0.6
// 请求函数
// 使用匿名函数,防止污染空间
(() => {
    function request(url, data = null, method = "POST") {
        return new Promise((resolve, reject) => {
            if (method.toUpperCase() == "POST") {
                fetch(url, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(data),
                })
                    .then(
                        (data) => resolve(data.json()),
                        (error) => {
                            reject(error);
                        }
                    )
                    .catch((err) => {
                        console.error("请求失败:", err);
                    });
            }
        });
    }

    // 弹出提示信息
    async function showMessage(msg) {
        await request("/api/notification/pushMsg", { msg, timeout: 3000 });
    }

    // 获取当前文档id
    function getFileID() {
        //获取当前页面
        const currentPage = getCurrentPage();
        //获取当前页面id
        const currentPageID = currentPage.querySelector(
            "span.protyle-breadcrumb__item--active"
        ).getAttribute("data-node-id");

        return currentPageID;
    }

    function getCurrentPage() {
        //获取当前屏幕
        const currentScreen = document.querySelector(".layout__wnd--active");
        //获取当前页面
        const currentPage = currentScreen.querySelector(
            ".fn__flex-1.protyle:not(.fn__none)"
        );
        return currentPage;
    }

    //传入 node节点列表 arr和数据抽取函数 fliter。
    // fliter 传入一个节点,返回一个字典
    // 字典格式:
    // {status,data}
    // status 为 ok,则代表为抽取成功,将data压入返回列表anwer,并最后返回。
    function iterArr(arr, fliter) {

        if (!arr && arr.length <= 0) {
            return [];
        }

        length = arr.length;
        anwer = [];
        for (let node of arr) {
            data = fliter(node);
            if (data["status"] === "ok" && data["data"]) {
                anwer.push(data["data"]);
            }
        }
        return anwer;
    }

    // 传入列表下的段落,返回被标记段落的父节点:列表项的id
    function fliterListCard(node) {
        var re = /(>>|(》》))\s?\u200b?$/;
        if (re.test(node.innerText)) {
            return {
                status: "ok",
                data: node.parentElement.getAttribute("data-node-id")
            }
        }
        return { status: "no", data: null };
    }

    function fliterMarkCard(node) {
        return {
            status: "ok",
            data: node.parentElement.parentElement.getAttribute("data-node-id")
        }
    }

    function fliterSuperBlockCard(node) {
        return {
            status: "ok",
            data: node.getAttribute("data-node-id")
        }
    }

    // 获取当前文档路径
    async function getHpath() {
        currentPageID = getFileID()
        body = { "id": currentPageID }
        let res = await request("/api/filetree/getHPathByID", body);

        //检测返回值
        if (res.code === 0) {
            let currentPagePath = res.data;
            return currentPagePath;
        }
        return null;
    }

    // 添加多个卡片
    async function addCards() {
        //获取全部deck
        let res = await request("/api/riff/getRiffDecks", {});

        //获取当前打开文档的路径
        focusItemPath = await getHpath()
        if (focusItemPath === null) {
            showMessage(`未能当前文档读取路径`);
            return;
        }

        //获取deck.id, 其deck.name 包含在 focusItemPath 中
        let customDeckIdArr = [];
        Array.from(res.data).forEach((item) => {
            let name = item.name;
            if (focusItemPath.search(name) != -1) {
                customDeckIdArr.push(item.id);
            }
        })

        if (customDeckIdArr.length === 0) {
            showMessage(`未能获取到:${focusItemPath} 的卡包`);
            return;
        }

        // 获取需要被制成卡片的块的ID
        const currentPage = getCurrentPage()
        //标记块
        const markList = currentPage.querySelectorAll("span[data-type='mark']")
        //超级块
        const superBlockList = currentPage.querySelectorAll("div[class='sb']")
        //列表块
        const listList = currentPage.querySelectorAll("div[class='li']:has(div[class='li'])  > div[class = 'p']")

        let arr = [];
        var markCardList = iterArr(markList, fliterMarkCard)
        var superBlockCardList = iterArr(superBlockList, fliterSuperBlockCard)
        var listCardList = iterArr(listList, fliterListCard)
        arr = arr.concat(markCardList, superBlockCardList, listCardList)


        if (arr.length === 0) {
            showMessage(`未能获取到:${focusItemPath} 页面的卡片`);
            return;
        }

        // 去重
        arr = [... new Set(arr)];

        for (deckIndex in customDeckIdArr) {

            // 把标记所在块全部制成卡片
            let body = {
                deckID: customDeckIdArr[deckIndex],
                blockIDs: arr,
            };
            let res = await request("/api/riff/addRiffCards", body);
            if (res.code === 0) {
                showMessage(`${res.data.name}卡包的总卡片数:${res.data.size}`);
            }
        }

    }

    // 添加一个按钮
    const barMode = document.getElementById("barMode");
    barMode.insertAdjacentHTML(
        "beforebegin",
        '<div id="addCards" class="toolbar__item b3-tooltips b3-tooltips__se" aria-label="制作卡片" ></div>'
    );
    const addCardsBtn = document.getElementById("addCards");
    addCardsBtn.style.width = "auto";
    const addCardsBtnIcon = `<svg t="1674130669751" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4526" width="200" height="200"><path d="M928 160H96a32 32 0 0 0-32 32v672a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32V192a32 32 0 0 0-32-32z m-32 672H128V224h768v608z" p-id="4527" fill="#9aa0a6"></path><path d="M230.592 448.096H544a32 32 0 1 0 0-64H230.592a32 32 0 0 0 0 64zM230.592 640.096H544a32 32 0 1 0 0-64H230.592a32 32 0 1 0 0 64zM768 704a32 32 0 0 0 32-32V350.016a32 32 0 1 0-64 0V672a32 32 0 0 0 32 32z" p-id="4528" fill="#9aa0a6"></path></svg>`;
    addCardsBtn.innerHTML = addCardsBtnIcon;
    addCardsBtn.addEventListener(
        "click",
        (e) => {
            addCards();
            e.stopPropagation();
            e.preventDefault();
        },
        true
    );

})();
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    28447 引用 • 119791 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • OneNote
    2 引用 • 5 回帖
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 51 关注
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    6 引用 • 26 回帖 • 559 关注
  • 招聘

    哪里都缺人,哪里都不缺人。

    188 引用 • 1057 回帖 • 2 关注
  • Sillot

    Insights(注意当前设置 master 为默认分支)

    汐洛彖夲肜矩阵(Sillot T☳Converbenk Matrix),致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点。其中汐洛绞架(Sillot-Gibbet)基于自思源笔记(siyuan-note),前身是思源笔记汐洛版(更早是思源笔记汐洛分支),是智慧新录乄终端(多端融合,移动端优先)。

    主仓库地址:Hi-Windom/Sillot

    文档地址:sillot.db.sc.cn

    注意事项:

    1. ⚠️ 汐洛仍在早期开发阶段,尚不稳定
    2. ⚠️ 汐洛并非面向普通用户设计,使用前请了解风险
    3. ⚠️ 汐洛绞架基于思源笔记,开发者尽最大努力与思源笔记保持兼容,但无法实现 100% 兼容
    29 引用 • 25 回帖 • 152 关注
  • IBM

    IBM(国际商业机器公司)或万国商业机器公司,简称 IBM(International Business Machines Corporation),总公司在纽约州阿蒙克市。1911 年托马斯·沃森创立于美国,是全球最大的信息技术和业务解决方案公司,拥有全球雇员 30 多万人,业务遍及 160 多个国家和地区。

    17 引用 • 53 回帖 • 158 关注
  • Anytype
    3 引用 • 31 回帖 • 59 关注
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    11 引用 • 15 回帖
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 260 关注
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    502 引用 • 1397 回帖 • 241 关注
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    70 引用 • 193 回帖 • 405 关注
  • 一些有用的避坑指南。

    69 引用 • 93 回帖
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 120 关注
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    43 引用 • 130 回帖 • 259 关注
  • 酷鸟浏览器

    安全 · 稳定 · 快速
    为跨境从业人员提供专业的跨境浏览器

    3 引用 • 59 回帖 • 64 关注
  • ngrok

    ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。

    7 引用 • 63 回帖 • 668 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 123 关注
  • Laravel

    Laravel 是一套简洁、优雅的 PHP Web 开发框架。它采用 MVC 设计,是一款崇尚开发效率的全栈框架。

    19 引用 • 23 回帖 • 770 关注
  • OnlyOffice
    4 引用 • 41 关注
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 636 关注
  • 阿里巴巴

    阿里巴巴网络技术有限公司(简称:阿里巴巴集团)是以曾担任英语教师的马云为首的 18 人,于 1999 年在中国杭州创立,他们相信互联网能够创造公平的竞争环境,让小企业通过创新与科技扩展业务,并在参与国内或全球市场竞争时处于更有利的位置。

    43 引用 • 221 回帖 • 11 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    29 引用 • 202 回帖 • 53 关注
  • Shell

    Shell 脚本与 Windows/Dos 下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比 Windows 下的批处理更强大,比用其他编程程序编辑的程序效率更高,因为它使用了 Linux/Unix 下的命令。

    126 引用 • 83 回帖 • 2 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 724 关注
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 563 关注
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    188 引用 • 319 回帖 • 222 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 44 关注