From 42bb0153847b232246bd220cf02d2b3945b0da23 Mon Sep 17 00:00:00 2001
From: dragonwocky <thedragonring.bod@gmail.com>
Date: Mon, 18 Nov 2024 22:46:01 +1100
Subject: [PATCH] feat: port across indent guides (formerly indentation lines)

---
 src/core/menu/islands/Options.mjs             |   9 +-
 src/extensions/indent-guides/client.css       |  81 +++++++++++++
 src/extensions/indent-guides/client.mjs       |  68 +++++++++++
 src/extensions/indent-guides/mod.json         |  79 +++++++++++++
 src/extensions/indentation-lines/client.css   | 101 ----------------
 src/extensions/indentation-lines/client.mjs   | 109 ------------------
 .../indentation-lines/indentation-lines.jpg   | Bin 3585 -> 0 bytes
 src/extensions/indentation-lines/mod.json     |  72 ------------
 src/extensions/line-numbers/mod.json          |   4 +-
 src/extensions/outliner/client.mjs            |   2 +-
 src/registry.json                             |   1 +
 11 files changed, 240 insertions(+), 286 deletions(-)
 create mode 100644 src/extensions/indent-guides/client.css
 create mode 100644 src/extensions/indent-guides/client.mjs
 create mode 100644 src/extensions/indent-guides/mod.json
 delete mode 100644 src/extensions/indentation-lines/client.css
 delete mode 100644 src/extensions/indentation-lines/client.mjs
 delete mode 100644 src/extensions/indentation-lines/indentation-lines.jpg
 delete mode 100644 src/extensions/indentation-lines/mod.json

diff --git a/src/core/menu/islands/Options.mjs b/src/core/menu/islands/Options.mjs
index c06c38d..9c742a1 100644
--- a/src/core/menu/islands/Options.mjs
+++ b/src/core/menu/islands/Options.mjs
@@ -80,7 +80,14 @@ function Options({ mod }) {
   const { html, modDatabase, setState } = globalThis.__enhancerApi;
   return filterOptionsForRender(mod.options).map((opt) => {
     opt.label ??= camelToSentenceCase(opt.key);
-    if (opt.type === "heading") return html`<${Heading}>${opt.label}<//>`;
+    if (opt.type === "heading") {
+      return typeof opt.description === "string"
+        ? html`<div class="mb-[18px]">
+            <${Heading}>${opt.label}<//>
+            <${Description} innerHTML=${opt.description} />
+          </div>`
+        : html`<${Heading}>${opt.label}<//>`;
+    }
     const _get = async () => (await modDatabase(mod.id)).get(opt.key),
       _set = async (value) => {
         await (await modDatabase(mod.id)).set(opt.key, value);
diff --git a/src/extensions/indent-guides/client.css b/src/extensions/indent-guides/client.css
new file mode 100644
index 0000000..d3fe8ca
--- /dev/null
+++ b/src/extensions/indent-guides/client.css
@@ -0,0 +1,81 @@
+/**
+ * notion-enhancer: indent guides
+ * (c) 2020 Alexa Baldon <alnbaldon@gmail.com> (https://github.com/runargs)
+ * (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
+ * (https://notion-enhancer.github.io/) under the MIT license
+ */
+
+body {
+  --guide--style: solid;
+  --guide--color: var(--theme--fg-border);
+}
+
+/* add indent guides to nested blocks */
+.notion-header-block,
+.notion-sub_header-block,
+.notion-sub_sub_header-block,
+.notion-toggle-block,
+.notion-to_do-block,
+.notion-bulleted_list-block,
+.notion-numbered_list-block {
+  --guide--offset: 32px;
+  --guide--indent: 14px;
+  position: relative;
+  &:before {
+    content: "";
+    position: absolute;
+    height: calc(100% - var(--guide--offset));
+    top: var(--guide--offset);
+    margin-inline-start: var(--guide--indent);
+    border-left: 1px var(--guide--style) var(--guide--color);
+  }
+}
+
+.notion-header-block {
+  --guide--offset: 47px;
+}
+.notion-sub_header-block {
+  --guide--offset: 40px;
+}
+.notion-header-block,
+.notion-sub_header-block,
+.notion-sub_sub_header-block,
+.notion-toggle-block {
+  --guide--indent: 13.4px;
+}
+
+/* add indent guides to toc blocks & the outliner */
+.notion-table_of_contents-block
+  [contenteditable="false"]
+  a
+  > div:not([style*="margin-left: 0"]),
+.notion-enhancer--outliner-heading:not(.pl-\[18px\]) {
+  position: relative;
+  --guide--indent: -16px;
+  &:before {
+    content: "";
+    top: 0;
+    position: absolute;
+    height: 100%;
+    margin-inline-start: var(--guide--indent);
+    border-left: 1px var(--guide--style) var(--guide--color);
+  }
+}
+
+.notion-enhancer--outliner-heading:not(.pl-\[18px\]) {
+  --guide--indent: -12px;
+}
+
+/* add solid background to drag handles,
+otherwise guides show through underneath */
+[role="button"]:is([aria-label="Drag"], [aria-label^="Click to add below"]) {
+  position: relative;
+  &:before {
+    content: "";
+    z-index: -1;
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    background: var(--theme--bg-primary);
+  }
+}
diff --git a/src/extensions/indent-guides/client.mjs b/src/extensions/indent-guides/client.mjs
new file mode 100644
index 0000000..762c2b6
--- /dev/null
+++ b/src/extensions/indent-guides/client.mjs
@@ -0,0 +1,68 @@
+/**
+ * notion-enhancer: indent guides
+ * (c) 2020 Alexa Baldon <alnbaldon@gmail.com> (https://github.com/runargs)
+ * (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
+ * (https://notion-enhancer.github.io/) under the MIT license
+ */
+
+export default async function (api, db) {
+  const lineType = await db.get("lineType"),
+    rainbowMode = await db.get("rainbowMode");
+  document.body.style.setProperty("--guide--style", lineType.toLowerCase());
+
+  // switch (await db.get(["style"])) {
+  //   case "dashed":
+  //     style = "dashed";
+  //     break;
+  //   case "dotted":
+  //     style = "dotted";
+  //     break;
+  //   case "soft":
+  //     opacity = 0.25;
+  //     break;
+  //   case "rainbow":
+  //     opacity = 0.7;
+  //     rainbow = true;
+  //     break;
+  // }
+
+  // const colors = ['red', 'pink', 'purple', 'blue', 'green', 'yellow'];
+  // colors.push(...colors, ...colors, ...colors, 'gray');
+
+  // for (const listType of ['bulleted_list', 'numbered_list', 'to_do', 'toggle']) {
+  //   if (!(await db.get([listType]))) continue;
+  //   css += `
+  //     .notion-page-content .notion-${listType}-block > div > div:last-child::before {
+  //       border-left: 1px ${style} var(--indentation_lines--color, currentColor);
+  //       opacity: ${opacity};
+  //     }`;
+
+  //   if (rainbow) {
+  //     for (let i = 0; i < colors.length; i++) {
+  //       css += `
+  //         .notion-page-content ${`.notion-${listType}-block `.repeat(i + 1)}
+  //           > div > div:last-child::before {
+  //           --indentation_lines--color: var(--theme--text_${colors[i]});
+  //         }`;
+  //     }
+  //   }
+  // }
+
+  // if (await db.get(['toggle_header'])) {
+  //   css += `
+  //     .notion-page-content [class$=header-block] > div > div > div:last-child::before {
+  //       border-left: 1px ${style} var(--indentation_lines--color, currentColor);
+  //       opacity: ${opacity};
+  //     }`;
+
+  //   if (rainbow) {
+  //     for (let i = 0; i < colors.length; i++) {
+  //       css += `
+  //       .notion-page-content ${`[class$=header-block] `.repeat(i + 1)}
+  //         > div > div > div:last-child::before{
+  //         --indentation_lines--color: var(--theme--text_${colors[i]});
+  //       }`;
+  //     }
+  //   }
+  // }
+}
diff --git a/src/extensions/indent-guides/mod.json b/src/extensions/indent-guides/mod.json
new file mode 100644
index 0000000..6d43688
--- /dev/null
+++ b/src/extensions/indent-guides/mod.json
@@ -0,0 +1,79 @@
+{
+  "name": "Indent Guides",
+  "id": "35815b3b-3916-4dc6-8769-c9c2448f8b57",
+  "version": "0.3.0",
+  "description": "Marks list indentation with vertical lines to make it easy to follow.",
+  "tags": ["extension", "usability", "indentation-lines"],
+  "authors": [
+    {
+      "name": "dragonwocky",
+      "homepage": "https://dragonwocky.me/",
+      "avatar": "https://dragonwocky.me/avatar.jpg"
+    },
+    {
+      "name": "runargs",
+      "email": "alnbaldon@gmail.com",
+      "homepage": "http://github.com/runargs",
+      "avatar": "https://avatars.githubusercontent.com/u/39810066"
+    }
+  ],
+  "options": [
+    {
+      "type": "select",
+      "key": "lineType",
+      "description": "The line style to use for indent guides.",
+      "values": ["Solid", "Dashed", "Dotted"]
+    },
+    {
+      "type": "toggle",
+      "key": "rainbowMode",
+      "description": "By default, indent guides are coloured based on the current theme. Rainbow mode uses alternating colours at each indent level to better connect corresponding blocks in longer lists.",
+      "value": false
+    },
+    { "type": "heading", "label": "List Types" },
+    {
+      "type": "toggle",
+      "key": "to-doList",
+      "description": "Shows indent guides for Notion's to-do list blocks.",
+      "value": true
+    },
+    {
+      "type": "toggle",
+      "key": "bulletedList",
+      "description": "Shows indent guides for Notion's bulleted list blocks.",
+      "value": true
+    },
+    {
+      "type": "toggle",
+      "key": "numberedList",
+      "description": "Shows indent guides for Notion's numbered list blocks.",
+      "value": true
+    },
+    {
+      "type": "toggle",
+      "key": "toggleList",
+      "description": "Shows indent guides for Notion's toggle list blocks.",
+      "value": true
+    },
+    {
+      "type": "toggle",
+      "key": "toggleHeadings",
+      "description": "Shows indent guides for Notion's toggle heading blocks.",
+      "value": true
+    },
+    {
+      "type": "toggle",
+      "key": "tableOfContents",
+      "description": "Shows indent guides for Notion's table of contents blocks.",
+      "value": true
+    },
+    {
+      "type": "toggle",
+      "key": "outliner",
+      "description": "Shows indent guides for the Outliner's table of contents in the side panel.",
+      "value": true
+    }
+  ],
+  "clientStyles": ["client.css"],
+  "clientScripts": ["client.mjs"]
+}
diff --git a/src/extensions/indentation-lines/client.css b/src/extensions/indentation-lines/client.css
deleted file mode 100644
index bcb867a..0000000
--- a/src/extensions/indentation-lines/client.css
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * notion-enhancer: indentation lines
- * (c) 2020 Alexa Baldon <alnbaldon@gmail.com> (https://github.com/runargs)
- * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
- * (https://notion-enhancer.github.io/) under the MIT license
- */
-
-.notion-page-content .notion-bulleted_list-block > div > div:last-child,
-.notion-page-content .notion-numbered_list-block > div > div:last-child,
-.notion-page-content .notion-to_do-block > div > div:last-child,
-.notion-page-content .notion-toggle-block > div > div:last-child,
-.notion-page-content [class$='header-block'] > div > div > div:last-child,
-.notion-page-content .notion-table_of_contents-block > div > div > a > div > div {
-  position: relative;
-}
-
-.notion-page-content .notion-bulleted_list-block > div > div:last-child::before,
-.notion-page-content .notion-numbered_list-block > div > div:last-child::before,
-.notion-page-content .notion-to_do-block > div > div:last-child::before,
-.notion-page-content .notion-toggle-block > div > div:last-child::before,
-.notion-page-content [class$='header-block'] > div > div > div:last-child::before,
-.notion-page-content .pseudoSelection > div > div:last-child::before {
-  content: '';
-  position: absolute;
-  height: calc(100% - 2em);
-  top: 2em;
-  margin-inline-start: -14.48px;
-}
-.notion-page-content [class$='header-block'] > div > div > div:last-child::before {
-  margin-inline-start: -15.48px;
-}
-
-.notion-page-content
-  .notion-table_of_contents-block
-  > div
-  > div
-  > a
-  > div
-  > div:not([style*='margin-left: 0px'])
-  > div::before {
-  content: '';
-  position: absolute;
-  height: 100%;
-  top: 0;
-  margin-inline-start: -14.48px;
-}
-
-/* add background to block dragger */
-.notion-frame
-  > [style*='position: absolute; top: 0px; left: 0px;']
-  [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;']
-  [data-block-id],
-.notion-peek-renderer
-  > div
-  > [style*='position: absolute; top: 0px; left: 0px;']
-  [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;']
-  [data-block-id] {
-  background: var(--theme--bg) !important;
-  border-radius: 3px;
-}
-.notion-frame
-  > [style*='position: absolute; top: 0px; left: 0px;']
-  [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;']
-  + .notion-focusable,
-.notion-peek-renderer
-  > div
-  > [style*='position: absolute; top: 0px; left: 0px;']
-  [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;']
-  + .notion-focusable {
-  left: -42px !important;
-  background: transparent !important;
-}
-.notion-frame
-  > [style*='position: absolute; top: 0px; left: 0px;']
-  [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;']
-  + .notion-focusable
-  > .plus,
-.notion-peek-renderer
-  > div
-  > [style*='position: absolute; top: 0px; left: 0px;']
-  [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;']
-  + .notion-focusable
-  > .plus {
-  border-radius: 3px;
-  width: 18px !important;
-  padding: 0 1px !important;
-  background: var(--theme--bg) !important;
-}
-.notion-frame
-  > [style*='position: absolute; top: 0px; left: 0px;']
-  [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;']
-  + .notion-focusable
-  > .plus:hover,
-.notion-peek-renderer
-  > div
-  > [style*='position: absolute; top: 0px; left: 0px;']
-  [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;']
-  + .notion-focusable
-  > .plus:hover {
-  background: var(--theme--ui_interactive-hover) !important;
-}
diff --git a/src/extensions/indentation-lines/client.mjs b/src/extensions/indentation-lines/client.mjs
deleted file mode 100644
index 3cb8736..0000000
--- a/src/extensions/indentation-lines/client.mjs
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * notion-enhancer: indentation lines
- * (c) 2020 Alexa Baldon <alnbaldon@gmail.com> (https://github.com/runargs)
- * (https://notion-enhancer.github.io/) under the MIT license
- */
-
-'use strict';
-
-export default async function ({ web }, db) {
-  let style = 'solid',
-    opacity = 1,
-    rainbow = false;
-  switch (await db.get(['style'])) {
-    case 'dashed':
-      style = 'dashed';
-      break;
-    case 'dotted':
-      style = 'dotted';
-      break;
-    case 'soft':
-      opacity = 0.25;
-      break;
-    case 'rainbow':
-      opacity = 0.7;
-      rainbow = true;
-      break;
-  }
-
-  let css = '';
-  const colors = ['red', 'pink', 'purple', 'blue', 'green', 'yellow'];
-  colors.push(...colors, ...colors, ...colors, 'gray');
-
-  for (const listType of ['bulleted_list', 'numbered_list', 'to_do', 'toggle']) {
-    if (!(await db.get([listType]))) continue;
-    css += `
-      .notion-page-content .notion-${listType}-block > div > div:last-child::before {
-        border-left: 1px ${style} var(--indentation_lines--color, currentColor);
-        opacity: ${opacity};
-      }`;
-
-    if (rainbow) {
-      for (let i = 0; i < colors.length; i++) {
-        css += `
-          .notion-page-content ${`.notion-${listType}-block `.repeat(i + 1)}
-            > div > div:last-child::before {
-            --indentation_lines--color: var(--theme--text_${colors[i]});
-          }`;
-      }
-    }
-  }
-
-  if (await db.get(['toggle_header'])) {
-    css += `
-      .notion-page-content [class$=header-block] > div > div > div:last-child::before {
-        border-left: 1px ${style} var(--indentation_lines--color, currentColor);
-        opacity: ${opacity};
-      }`;
-
-    if (rainbow) {
-      for (let i = 0; i < colors.length; i++) {
-        css += `
-        .notion-page-content ${`[class$=header-block] `.repeat(i + 1)}
-          > div > div > div:last-child::before{
-          --indentation_lines--color: var(--theme--text_${colors[i]});
-        }`;
-      }
-    }
-  }
-
-  if (await db.get(['table_of_contents'])) {
-    css += `
-      .notion-page-content .notion-table_of_contents-block > div > div > a > div
-        > div:not([style*='margin-left: 0px']) > div::before {
-        border-left: 1px ${style} var(--indentation_lines--color, currentColor);
-        opacity: ${opacity};
-      }`;
-
-    if (rainbow) {
-      css += `
-        .notion-page-content .notion-table_of_contents-block > div > div > a > div
-          > div[style*='margin-left: 24px'] > div::before {
-          --indentation_lines--color: var(--theme--text_${colors[0]});
-        }
-        .notion-page-content .notion-table_of_contents-block > div > div > a > div
-          > div[style*='margin-left: 48px'] > div::before {
-          --indentation_lines--color: var(--theme--text_${colors[1]});
-        }`;
-    }
-  }
-
-  if (await db.get(['outliner'])) {
-    css += `
-      .outliner--header:not([style='--outliner--indent:0px;'])::before {
-        border-left: 1px ${style} var(--indentation_lines--color, currentColor);
-        opacity: ${opacity};
-      }`;
-    if (rainbow) {
-      css += `
-        .outliner--header[style='--outliner--indent:18px;']::before {
-          --indentation_lines--color: var(--theme--text_${colors[0]});
-        }
-        .outliner--header[style='--outliner--indent:36px;']::before {
-          --indentation_lines--color: var(--theme--text_${colors[1]});
-        }`;
-    }
-  }
-
-  web.render(document.head, web.html`<style>${css}</style>`);
-}
diff --git a/src/extensions/indentation-lines/indentation-lines.jpg b/src/extensions/indentation-lines/indentation-lines.jpg
deleted file mode 100644
index 0587e6dbbd9f9ea2ee2bdd29c67174e7e2a84230..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 3585
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
Q@4WesssBF?iH-h$0wwe7ga7~l

diff --git a/src/extensions/indentation-lines/mod.json b/src/extensions/indentation-lines/mod.json
deleted file mode 100644
index 33a82d9..0000000
--- a/src/extensions/indentation-lines/mod.json
+++ /dev/null
@@ -1,72 +0,0 @@
-{
-  "name": "indentation lines",
-  "id": "35815b3b-3916-4dc6-8769-c9c2448f8b57",
-  "version": "0.2.0",
-  "description": "adds vertical relationship lines to make list trees easier to follow.",
-  "preview": "indentation-lines.jpg",
-  "tags": ["extension", "usability"],
-  "authors": [
-    {
-      "name": "runargs",
-      "email": "alnbaldon@gmail.com",
-      "homepage": "http://github.com/runargs",
-      "avatar": "https://avatars.githubusercontent.com/u/39810066"
-    }
-  ],
-  "css": {
-    "client": ["client.css"]
-  },
-  "js": {
-    "client": ["client.mjs"]
-  },
-  "options": [
-    {
-      "type": "select",
-      "key": "style",
-      "label": "style",
-      "values": ["solid", "dashed", "dotted", "soft", "rainbow"]
-    },
-    {
-      "type": "toggle",
-      "key": "bulleted_list",
-      "label": "bulleted lists",
-      "value": true
-    },
-    {
-      "type": "toggle",
-      "key": "numbered_list",
-      "label": "numbered lists",
-      "value": true
-    },
-    {
-      "type": "toggle",
-      "key": "to_do",
-      "label": "to-do lists",
-      "value": true
-    },
-    {
-      "type": "toggle",
-      "key": "toggle",
-      "label": "toggle lists",
-      "value": true
-    },
-    {
-      "type": "toggle",
-      "key": "toggle_header",
-      "label": "toggle headers",
-      "value": true
-    },
-    {
-      "type": "toggle",
-      "key": "table_of_contents",
-      "label": "tables of contents",
-      "value": true
-    },
-    {
-      "type": "toggle",
-      "key": "outliner",
-      "label": "outliner (panel extension)",
-      "value": true
-    }
-  ]
-}
diff --git a/src/extensions/line-numbers/mod.json b/src/extensions/line-numbers/mod.json
index cc2d35c..3f23b03 100755
--- a/src/extensions/line-numbers/mod.json
+++ b/src/extensions/line-numbers/mod.json
@@ -20,13 +20,13 @@
     {
       "type": "toggle",
       "key": "numberSingleLines",
-      "description": "Add line numbers to code blocks with only one line.",
+      "description": "Adds line numbers to code blocks with only one line.",
       "value": true
     },
     {
       "type": "select",
       "key": "decorationStyle",
-      "description": "Decorate line numbers with additional styling to distinguish them from code block content.",
+      "description": "Decorates line numbers with additional styling to distinguish them from code block content.",
       "values": ["Border", "Background", "None"]
     }
   ],
diff --git a/src/extensions/outliner/client.mjs b/src/extensions/outliner/client.mjs
index 7be9953..7cf677b 100644
--- a/src/extensions/outliner/client.mjs
+++ b/src/extensions/outliner/client.mjs
@@ -75,7 +75,7 @@ export default async (api, db) => {
     },
     getBlockOffset = ($block) => {
       let offset = 0;
-      while (!$block.matches("[data-content-editable-root]")) {
+      while (!$block?.matches("[data-content-editable-root]")) {
         offset += $block.offsetTop;
         $block = $block.offsetParent;
       }
diff --git a/src/registry.json b/src/registry.json
index f353a24..86f9dda 100644
--- a/src/registry.json
+++ b/src/registry.json
@@ -7,6 +7,7 @@
   "extensions/outliner",
   "extensions/word-counter",
   "extensions/line-numbers",
+  "extensions/indent-guides",
   "extensions/right-to-left",
   "extensions/no-peeking",
   "extensions/focus",