diff --git a/README.md b/README.md
index dd0058f..81b6da2 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# Obsidian Admonition
-Adds admonition block-styled content to Obsidian.md
+Adds admonition block-styled content to Obsidian.md, styled after [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/reference/admonitions/)
+
+![](https://raw.githubusercontent.com/valentine195/obsidian-admonition/master/images/all.gif)
## Usage
@@ -12,9 +14,19 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla.
```
````
-This will then render as the admonition:
+Becomes:
+
+![](https://raw.githubusercontent.com/valentine195/obsidian-admonition/master/images/default.png)
-
+## Options
+
+````markdown
+``` # Admonition type. See below for a list of available types.
+title: # Admonition title. Leave blank to remove the title element and display only the content.
+collapse: # Create a collapsible admonition. Use "open" to initially render the admonition open.
+content: # Actual text of admonition. Only required if "title" or "collapse" is used.
+```
+````
### Titles
@@ -27,9 +39,10 @@ content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euism
```
````
-
+![](https://raw.githubusercontent.com/valentine195/obsidian-admonition/master/images/title.png)
Leave the title field blank to only display the admonition.
+
````markdown
```note
title:
@@ -37,17 +50,21 @@ content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euism
```
````
-
+![](https://raw.githubusercontent.com/valentine195/obsidian-admonition/master/images/no-title.png)
-**Please note that when the title is included, you _must_ specificy the content as well.**
+**Please note that when the title is included, you _must_ specify the content as well.**
### Collapsible
-Use `collapse: open` or `collapse: closed` to create a collapsible admonition.
+Use the `collapse` parameter to create a collapsible admonition.
+
+`collapse: open` will start the admonition opened on render, but allow collapse on click.
+
+If a blank title is provided, the collapse parameter will not do anything.
-`collapse: open` will startthe admonition opened on render.
+![](https://raw.githubusercontent.com/valentine195/obsidian-admonition/master/images/collapse.gif)
-
+**Please note that when the title is included, you _must_ specify the content as well.**
## Admonition Types
diff --git a/manifest.json b/manifest.json
index 7d5b38f..6caace0 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "obsidian-admonition",
"name": "Admonition",
- "version": "0.0.5",
+ "version": "0.1.0",
"minAppVersion": "0.11.0",
"description": "Admonition block-styled content for Obsidian.md",
"author": "Jeremy Valentine",
diff --git a/package.json b/package.json
index 2eda7ec..fce6d4e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "obsidian-admonition",
- "version": "0.0.5",
+ "version": "0.1.0",
"description": "Admonition block-styled content for Obsidian.md",
"main": "main.js",
"scripts": {
diff --git a/src/admonitions.ts b/src/admonitions.ts
deleted file mode 100644
index 25b37a6..0000000
--- a/src/admonitions.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-interface Admonition {
- [admonitionType: string]: string;
-}
-const admonitions = [
- "note",
- "seealso",
- "abstract",
- "summary",
- "info",
- "todo",
- "tip",
- "hint",
- "important",
- "success",
- "check",
- "done",
- "question",
- "help",
- "faq",
- "warning",
- "caution",
- "attention",
- "failure",
- "fail",
- "missing",
- "danger",
- "error",
- "bug",
- "example",
- "quote",
- "cite"
-] as const;
-
-const ADMONITION_MAP: Record = {
- note: "note",
- seealso: "note",
- abstract: "abstract",
- summary: "abstract",
- info: "info",
- todo: "todo",
- tip: "tip",
- hint: "tip",
- important: "tip",
- success: "success",
- check: "check",
- done: "done",
- question: "question",
- help: "question",
- faq: "question",
- warning: "warning",
- caution: "warning",
- attention: "warning",
- failure: "failure",
- fail: "failure",
- missing: "failure",
- danger: "danger",
- error: "danger",
- bug: "bug",
- example: "example",
- quote: "quote",
- cite: "quote"
-};
-export default ADMONITION_MAP as Admonition;
diff --git a/src/main.ts b/src/main.ts
index 8602aba..996a083 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,48 +1,112 @@
-import { MarkdownPostProcessorContext, Plugin } from "obsidian";
+import { MarkdownPostProcessorContext, Notice, Plugin } from "obsidian";
import "./main.css";
-import ADMONITION_MAP from "./admonitions";
-export default class Admonition extends Plugin {
+const ADMONITION_MAP: {
+ [admonitionType: string]: string;
+} = {
+ note: "note",
+ seealso: "note",
+ abstract: "abstract",
+ summary: "abstract",
+ info: "info",
+ todo: "todo",
+ tip: "tip",
+ hint: "tip",
+ important: "tip",
+ success: "success",
+ check: "check",
+ done: "done",
+ question: "question",
+ help: "question",
+ faq: "question",
+ warning: "warning",
+ caution: "warning",
+ attention: "warning",
+ failure: "failure",
+ fail: "failure",
+ missing: "failure",
+ danger: "danger",
+ error: "danger",
+ bug: "bug",
+ example: "example",
+ quote: "quote",
+ cite: "quote"
+};
+const classMap = Object.keys(ADMONITION_MAP).map((k) => `language-${k}`);
+
+/** Fast Intersection taken from
+ * https://codeburst.io/optimizing-array-analytics-in-javascript-part-two-search-intersection-and-cross-products-79b4a6d68da0
+ */
+const fastIntersection = (...arrays: any[]) => {
+ // if we process the arrays from shortest to longest
+ // then we will identify failure points faster, i.e. when
+ // one item is not in all arrays
+ const ordered =
+ arrays.length === 1
+ ? arrays
+ : arrays.sort((a1, a2) => a1.length - a2.length),
+ shortest = ordered[0],
+ set = new Set(), // used for bookeeping, Sets are faster
+ result = []; // the intersection, conversion from Set is slow
+ // for each item in the shortest array
+ for (let i = 0; i < shortest.length; i++) {
+ const item = shortest[i];
+ // see if item is in every subsequent array
+ let every = true; // don't use ordered.every ... it is slow
+ for (let j = 1; j < ordered.length; j++) {
+ if (ordered[j].includes(item)) continue;
+ every = false;
+ break;
+ }
+ // ignore if not in every other array, or if already captured
+ if (!every || set.has(item)) continue;
+ // otherwise, add to bookeeping set and the result
+ set.add(item);
+ result[result.length] = item;
+ }
+ return result;
+};
+export default class ObsidianAdmonition extends Plugin {
async onload(): Promise {
console.log("Obsidian Admonition loaded");
this.registerMarkdownPostProcessor(this.postprocessor.bind(this));
}
postprocessor(el: HTMLElement, ctx: MarkdownPostProcessorContext) {
- /* */
+ //don't process if no code elements in element;
let codeBlocks = el.querySelectorAll("code");
if (!codeBlocks.length) return;
- const classMap = Object.keys(ADMONITION_MAP).map(
- (k) => `language-${k}`
- );
- codeBlocks = Array.prototype.map
- .call(
- codeBlocks,
- (element: HTMLElement): HTMLElement => {
- if (element) {
- const classList = Array.prototype.filter.call(
- element.classList,
- (cls: string) => classMap.includes(cls)
- );
- if (classList.length) return element;
- }
- }
- )
- .filter((b: HTMLElement) => b);
+ //don't process if the code block is not an admonition type
+ codeBlocks = Array.prototype.filter.call(
+ codeBlocks,
+ (element: HTMLElement) =>
+ element &&
+ fastIntersection(
+ Array.prototype.slice.call(element.classList),
+ classMap
+ ).length > 0
+ );
if (!codeBlocks.length) return;
+ //render admonition element
codeBlocks.forEach((block) => {
if (block) {
- let classType = Array.prototype.find.call(
- block.classList,
- (cls: string) => classMap.includes(cls)
- );
- if (!classType) return;
let type =
- ADMONITION_MAP[classType.split("language-").pop().trim()];
- if (!type) return;
+ ADMONITION_MAP[
+ Array.prototype.find
+ .call(block.classList, (cls: string) =>
+ classMap.includes(cls)
+ )
+ .split("language-")
+ .pop()
+ .trim()
+ ];
+ if (!type) {
+ new Notice("There was an error rendering the admonition.");
+ return;
+ }
let params = Object.fromEntries(
block.innerText
.split("\n")
@@ -53,13 +117,6 @@ export default class Admonition extends Plugin {
content = block.innerText,
collapse
} = params;
- console.log(
- "🚀 ~ file: main.ts ~ line 56 ~ Admonition ~ codeBlocks.forEach ~ params",
- params,
- block.innerText
- .split("\n")
- .map((l) => l.split(":").map((s) => s.trim()))
- );
if (
Object.prototype.hasOwnProperty.call(params, "title") &&
@@ -71,14 +128,12 @@ export default class Admonition extends Plugin {
if (
Object.prototype.hasOwnProperty.call(params, "collapse") &&
(params.collapse.length == 0 ||
- params.collapse === undefined)
+ params.collapse === undefined ||
+ collapse !== "open")
) {
collapse = "closed";
}
- console.log(
- "🚀 ~ file: main.ts ~ line 69 ~ Admonition ~ codeBlocks.forEach ~ params.collapse",
- collapse
- );
+
this.buildAdmonition(
block.parentElement,
type,
@@ -97,15 +152,9 @@ export default class Admonition extends Plugin {
collapse?: string
) {
let attrs,
- els = [
- "div" as keyof HTMLElementTagNameMap,
- "div" as keyof HTMLElementTagNameMap
- ];
- if (collapse && ["open", "closed"].includes(collapse)) {
- els = [
- "details" as keyof HTMLElementTagNameMap,
- "summary" as keyof HTMLElementTagNameMap
- ];
+ els: Array = ["div", "div"];
+ if (collapse) {
+ els = ["details", "summary"];
attrs = {
[collapse]: true
};
diff --git a/versions.json b/versions.json
index fdb2e69..cca8d52 100644
--- a/versions.json
+++ b/versions.json
@@ -1,3 +1,3 @@
{
- "0.0.5": "0.11.0"
+ "0.1.0": "0.11.0"
}