notion-kroki

Render notion code block as graph by kroki

  1. // ==UserScript==
  2. // @name notion-kroki
  3. // @namespace https://github.com/zuisong/notion-kroki
  4. // @homepage https://github.com/zuisong/notion-kroki
  5. // @homepageURL https://github.com/zuisong/notion-kroki
  6. // @source https://github.com/zuisong/notion-kroki
  7. // @contributionURL https://github.com/zuisong/notion-kroki
  8. // @grant none
  9. // @version 1.2.0
  10. // @license MIT
  11. // @match *://www.notion.so/*
  12. // @match *://*.notion.site/*
  13. // @match *://*.super.site/*
  14. // @supportURL https://github.com/zuisong/notion-kroki/issues
  15. // @require https://unpkg.com/fflate@0.8.0/umd/index.js
  16. // @run-at document-idle
  17. // @author zuisong
  18. // @description Render notion code block as graph by kroki
  19. // ==/UserScript==
  20. function debounce(func, wait) {
  21. let timeoutId;
  22. return function debounced() {
  23. for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
  24. args[_key] = arguments[_key];
  25. }
  26. clearTimeout(timeoutId);
  27. timeoutId = setTimeout(()=>{
  28. timeoutId = undefined;
  29. func(...args);
  30. }, wait);
  31. };
  32. }
  33. function _debug() {
  34. for(var _len = arguments.length, data = new Array(_len), _key = 0; _key < _len; _key++){
  35. data[_key] = arguments[_key];
  36. }
  37. if (isDebugMode()) {
  38. console.log(...data);
  39. }
  40. }
  41. function isDebugMode() {
  42. return !!localStorage.getItem("debug");
  43. }
  44. const defaultConfig = {
  45. serverPath: "//kroki.io/"
  46. };
  47. function main() {
  48. let element = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : null;
  49. const blocks = Array.from((element || document.body).querySelectorAll("*")).filter((it)=>it.textContent.startsWith("//kroki "));
  50. for (const codeDiv of blocks){
  51. const lines = codeDiv.textContent.split("\n");
  52. const type = lines[0].replace("//kroki", "").trim();
  53. if (!type.trim()) continue;
  54. const data = lines.filter((_value, index)=>index !== 0).join("\n");
  55. if (!data.trim()) continue;
  56. const svgUrl = plant(data, type, defaultConfig);
  57. const div = document.createElement("div", undefined);
  58. div.setAttribute("style", "display: flex; flex-direction: row; place-content: center;");
  59. div.setAttribute("notion-kroki", "true");
  60. div.innerHTML = `<object type="image/svg+xml" style="max-width: 100%;" data="${svgUrl}" />`;
  61. const parentElement = codeDiv.parentElement.parentElement;
  62. const preCreatedNode = parentElement.querySelector("div[notion-kroki]");
  63. if (preCreatedNode) {
  64. const preSvgUrl = preCreatedNode.firstElementChild.getAttribute("data");
  65. _debug(`preSvgUrl:${preSvgUrl}`);
  66. _debug(`svgUrl:${svgUrl}`);
  67. if (preSvgUrl === svgUrl) {
  68. continue;
  69. } else {
  70. parentElement.removeChild(preCreatedNode);
  71. }
  72. }
  73. parentElement.appendChild(div);
  74. }
  75. }
  76. function textEncode(str) {
  77. return new TextEncoder().encode(str);
  78. }
  79. function plant(content, type, config) {
  80. _debug(`kroki render type: ${type}`);
  81. _debug(`kroki render content:\n${content}`);
  82. const urlPrefix = `${config.serverPath + type}/svg/`;
  83. const data = textEncode(content);
  84. const compressed = strFromU8(fflate.zlibSync(data, {
  85. level: 9
  86. }));
  87. const result = btoa(compressed).replace(/\+/g, "-").replace(/\//g, "_");
  88. const svgUrl = urlPrefix + result;
  89. return svgUrl;
  90. }
  91. function init_listener() {
  92. if (globalThis.MutationObserver) {
  93. new MutationObserver(check).observe(document, {
  94. childList: true,
  95. subtree: true
  96. });
  97. }
  98. }
  99. const render = debounce(main, 100);
  100. function check(mutations, _observer) {
  101. render();
  102. }
  103. function strFromU8(dat) {
  104. let r = "";
  105. const s = 2 ** 15;
  106. for(let i = 0; i < dat.length; i += s){
  107. r += String.fromCharCode(...dat.subarray(i, i + s));
  108. }
  109. return r;
  110. }
  111. main();
  112. init_listener();
  113.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址