commit
7988eb4033
|
@ -0,0 +1,500 @@
|
|||
const slugify = require("@sindresorhus/slugify");
|
||||
const markdownIt = require("markdown-it");
|
||||
const fs = require("fs");
|
||||
const matter = require("gray-matter");
|
||||
const faviconsPlugin = require("eleventy-plugin-gen-favicons");
|
||||
const tocPlugin = require("eleventy-plugin-nesting-toc");
|
||||
const { parse } = require("node-html-parser");
|
||||
const htmlMinifier = require("html-minifier");
|
||||
const pluginRss = require("@11ty/eleventy-plugin-rss");
|
||||
|
||||
const { headerToId, namedHeadingsFilter } = require("./src/helpers/utils");
|
||||
const {
|
||||
userMarkdownSetup,
|
||||
userEleventySetup,
|
||||
} = require("./src/helpers/userSetup");
|
||||
|
||||
const Image = require("@11ty/eleventy-img");
|
||||
function transformImage(src, cls, alt, sizes, widths = ["500", "700", "auto"]) {
|
||||
let options = {
|
||||
widths: widths,
|
||||
formats: ["webp", "jpeg"],
|
||||
outputDir: "./dist/img/optimized",
|
||||
urlPath: "/img/optimized",
|
||||
};
|
||||
|
||||
// generate images, while this is async we don’t wait
|
||||
Image(src, options);
|
||||
let metadata = Image.statsSync(src, options);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
const tagRegex = /(^|\s|\>)(#[^\s!@#$%^&*()=+\.,\[{\]};:'"?><]+)(?!([^<]*>))/g;
|
||||
|
||||
module.exports = function (eleventyConfig) {
|
||||
eleventyConfig.setLiquidOptions({
|
||||
dynamicPartials: true,
|
||||
});
|
||||
let markdownLib = markdownIt({
|
||||
breaks: true,
|
||||
html: true,
|
||||
linkify: true,
|
||||
})
|
||||
.use(require("markdown-it-anchor"), {
|
||||
slugify: headerToId,
|
||||
})
|
||||
.use(require("markdown-it-mark"))
|
||||
.use(require("markdown-it-footnote"))
|
||||
.use(function (md) {
|
||||
md.renderer.rules.hashtag_open = function (tokens, idx) {
|
||||
return '<a class="tag" onclick="toggleTagSearch(this)">';
|
||||
};
|
||||
})
|
||||
.use(require("markdown-it-mathjax3"), {
|
||||
tex: {
|
||||
inlineMath: [["$", "$"]],
|
||||
},
|
||||
options: {
|
||||
skipHtmlTags: { "[-]": ["pre"] },
|
||||
},
|
||||
})
|
||||
.use(require("markdown-it-attrs"))
|
||||
.use(require("markdown-it-task-checkbox"), {
|
||||
disabled: true,
|
||||
divWrap: false,
|
||||
divClass: "checkbox",
|
||||
idPrefix: "cbx_",
|
||||
ulClass: "task-list",
|
||||
liClass: "task-list-item",
|
||||
})
|
||||
.use(require("markdown-it-plantuml"), {
|
||||
openMarker: "```plantuml",
|
||||
closeMarker: "```",
|
||||
})
|
||||
.use(namedHeadingsFilter)
|
||||
.use(function (md) {
|
||||
//https://github.com/DCsunset/markdown-it-mermaid-plugin
|
||||
const origFenceRule =
|
||||
md.renderer.rules.fence ||
|
||||
function (tokens, idx, options, env, self) {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
};
|
||||
md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
|
||||
const token = tokens[idx];
|
||||
if (token.info === "mermaid") {
|
||||
const code = token.content.trim();
|
||||
return `<pre class="mermaid">${code}</pre>`;
|
||||
}
|
||||
if (token.info === "transclusion") {
|
||||
const code = token.content.trim();
|
||||
return `<div class="transclusion">${md.render(code)}</div>`;
|
||||
}
|
||||
if (token.info.startsWith("ad-")) {
|
||||
const code = token.content.trim();
|
||||
const parts = code.split("\n")
|
||||
let titleLine;
|
||||
let collapse;
|
||||
let collapsible = false
|
||||
let collapsed = true
|
||||
let icon;
|
||||
let color;
|
||||
let nbLinesToSkip = 0
|
||||
for (let i = 0; i<4; i++) {
|
||||
if (parts[i] && parts[i].trim()) {
|
||||
let line = parts[i] && parts[i].trim().toLowerCase()
|
||||
if (line.startsWith("title:")) {
|
||||
titleLine = line.substring(6);
|
||||
nbLinesToSkip ++;
|
||||
} else if (line.startsWith("icon:")) {
|
||||
icon = line.substring(5);
|
||||
nbLinesToSkip ++;
|
||||
} else if (line.startsWith("collapse:")) {
|
||||
collapsible = true
|
||||
collapse = line.substring(9);
|
||||
if (collapse && collapse.trim().toLowerCase() == 'open') {
|
||||
collapsed = false
|
||||
}
|
||||
nbLinesToSkip ++;
|
||||
} else if (line.startsWith("color:")) {
|
||||
color = line.substring(6);
|
||||
nbLinesToSkip ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
const foldDiv = collapsible ? `<div class="callout-fold">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-chevron-down">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</div>` : "";
|
||||
const titleDiv = titleLine
|
||||
? `<div class="callout-title"><div class="callout-title-inner">${titleLine}</div>${foldDiv}</div>`
|
||||
: "";
|
||||
let collapseClasses = titleLine && collapsible ? 'is-collapsible' : ''
|
||||
if (collapsible && collapsed) {
|
||||
collapseClasses += " is-collapsed"
|
||||
}
|
||||
|
||||
let res = `<div data-callout-metadata class="callout ${collapseClasses}" data-callout="${
|
||||
token.info.substring(3)
|
||||
}">${titleDiv}\n<div class="callout-content">${md.render(
|
||||
parts.slice(nbLinesToSkip).join("\n")
|
||||
)}</div></div>`;
|
||||
return res
|
||||
}
|
||||
|
||||
// Other languages
|
||||
return origFenceRule(tokens, idx, options, env, slf);
|
||||
};
|
||||
|
||||
const defaultImageRule =
|
||||
md.renderer.rules.image ||
|
||||
function (tokens, idx, options, env, self) {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
};
|
||||
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
||||
const imageName = tokens[idx].content;
|
||||
const [fileName, width] = imageName.split("|");
|
||||
if (width) {
|
||||
const widthIndex = tokens[idx].attrIndex("width");
|
||||
const widthAttr = `${width}px`;
|
||||
if (widthIndex < 0) {
|
||||
tokens[idx].attrPush(["width", widthAttr]);
|
||||
} else {
|
||||
tokens[idx].attrs[widthIndex][1] = widthAttr;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultImageRule(tokens, idx, options, env, self);
|
||||
};
|
||||
|
||||
const defaultLinkRule =
|
||||
md.renderer.rules.link_open ||
|
||||
function (tokens, idx, options, env, self) {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
};
|
||||
md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
|
||||
const aIndex = tokens[idx].attrIndex("target");
|
||||
const classIndex = tokens[idx].attrIndex("class");
|
||||
|
||||
if (aIndex < 0) {
|
||||
tokens[idx].attrPush(["target", "_blank"]);
|
||||
} else {
|
||||
tokens[idx].attrs[aIndex][1] = "_blank";
|
||||
}
|
||||
|
||||
if (classIndex < 0) {
|
||||
tokens[idx].attrPush(["class", "external-link"]);
|
||||
} else {
|
||||
tokens[idx].attrs[classIndex][1] = "external-link";
|
||||
}
|
||||
|
||||
return defaultLinkRule(tokens, idx, options, env, self);
|
||||
};
|
||||
})
|
||||
.use(userMarkdownSetup);
|
||||
|
||||
eleventyConfig.setLibrary("md", markdownLib);
|
||||
|
||||
eleventyConfig.addFilter("isoDate", function (date) {
|
||||
return date && date.toISOString();
|
||||
});
|
||||
|
||||
|
||||
eleventyConfig.addFilter("link", function (str) {
|
||||
return (
|
||||
str &&
|
||||
str.replace(/\[\[(.*?\|.*?)\]\]/g, function (match, p1) {
|
||||
//Check if it is an embedded excalidraw drawing or mathjax javascript
|
||||
if (p1.indexOf("],[") > -1 || p1.indexOf('"$"') > -1) {
|
||||
return match;
|
||||
}
|
||||
const [fileLink, linkTitle] = p1.split("|");
|
||||
|
||||
let fileName = fileLink.replaceAll("&", "&");
|
||||
let header = "";
|
||||
let headerLinkPath = "";
|
||||
if (fileLink.includes("#")) {
|
||||
[fileName, header] = fileLink.split("#");
|
||||
headerLinkPath = `#${headerToId(header)}`;
|
||||
}
|
||||
|
||||
let permalink = `/notes/${slugify(fileName)}`;
|
||||
let noteIcon = process.env.NOTE_ICON_DEFAULT;
|
||||
const title = linkTitle ? linkTitle : fileName;
|
||||
let deadLink = false;
|
||||
|
||||
try {
|
||||
const startPath = "./src/site/notes/";
|
||||
const fullPath = fileName.endsWith(".md")
|
||||
? `${startPath}${fileName}`
|
||||
: `${startPath}${fileName}.md`;
|
||||
const file = fs.readFileSync(fullPath, "utf8");
|
||||
const frontMatter = matter(file);
|
||||
if (frontMatter.data.permalink) {
|
||||
permalink = frontMatter.data.permalink;
|
||||
}
|
||||
if (
|
||||
frontMatter.data.tags &&
|
||||
frontMatter.data.tags.indexOf("gardenEntry") != -1
|
||||
) {
|
||||
permalink = "/";
|
||||
}
|
||||
if (frontMatter.data.noteIcon) {
|
||||
noteIcon = frontMatter.data.noteIcon;
|
||||
}
|
||||
} catch {
|
||||
deadLink = true;
|
||||
}
|
||||
|
||||
if(deadLink){
|
||||
return `<a class="internal-link is-unresolved" href="/404">${title}</a>`;
|
||||
}
|
||||
return `<a class="internal-link" data-note-icon="${noteIcon}" href="${permalink}${headerLinkPath}">${title}</a>`;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter("taggify", function (str) {
|
||||
return (
|
||||
str &&
|
||||
str.replace(tagRegex, function (match, precede, tag) {
|
||||
return `${precede}<a class="tag" onclick="toggleTagSearch(this)" data-content="${tag}">${tag}</a>`;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter("searchableTags", function (str) {
|
||||
let tags;
|
||||
let match = str && str.match(tagRegex);
|
||||
if (match) {
|
||||
tags = match
|
||||
.map((m) => {
|
||||
return `"${m.split("#")[1]}"`;
|
||||
})
|
||||
.join(", ");
|
||||
}
|
||||
if (tags) {
|
||||
return `${tags},`;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter("hideDataview", function (str) {
|
||||
return (
|
||||
str &&
|
||||
str.replace(/\(\S+\:\:(.*)\)/g, function (_, value) {
|
||||
return value.trim();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
eleventyConfig.addTransform("callout-block", function (str) {
|
||||
const parsed = parse(str);
|
||||
|
||||
const transformCalloutBlocks = (
|
||||
blockquotes = parsed.querySelectorAll("blockquote")
|
||||
) => {
|
||||
for (const blockquote of blockquotes) {
|
||||
transformCalloutBlocks(blockquote.querySelectorAll("blockquote"));
|
||||
|
||||
let content = blockquote.innerHTML;
|
||||
|
||||
let titleDiv = "";
|
||||
let calloutType = "";
|
||||
let isCollapsable;
|
||||
let isCollapsed;
|
||||
const calloutMeta = /\[!([\w-]*)\](\+|\-){0,1}(\s?.*)/;
|
||||
if (!content.match(calloutMeta)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
content = content.replace(
|
||||
calloutMeta,
|
||||
function (metaInfoMatch, callout, collapse, title) {
|
||||
isCollapsable = Boolean(collapse);
|
||||
isCollapsed = collapse === "-";
|
||||
const titleText = title.replace(/(<\/{0,1}\w+>)/, "")
|
||||
? title
|
||||
: `${callout.charAt(0).toUpperCase()}${callout
|
||||
.substring(1)
|
||||
.toLowerCase()}`;
|
||||
const fold = isCollapsable
|
||||
? `<div class="callout-fold"><i icon-name="chevron-down"></i></div>`
|
||||
: ``;
|
||||
|
||||
calloutType = callout;
|
||||
titleDiv = `<div class="callout-title"><div class="callout-title-inner">${titleText}</div>${fold}</div>`;
|
||||
return "";
|
||||
}
|
||||
);
|
||||
|
||||
blockquote.tagName = "div";
|
||||
blockquote.classList.add("callout");
|
||||
blockquote.classList.add(isCollapsable ? "is-collapsible" : "");
|
||||
blockquote.classList.add(isCollapsed ? "is-collapsed" : "");
|
||||
blockquote.setAttribute("data-callout", calloutType.toLowerCase());
|
||||
blockquote.innerHTML = `${titleDiv}\n<div class="callout-content">${content}</div>`;
|
||||
}
|
||||
};
|
||||
|
||||
transformCalloutBlocks();
|
||||
|
||||
return str && parsed.innerHTML;
|
||||
});
|
||||
|
||||
function fillPictureSourceSets(src, cls, alt, meta, width, imageTag) {
|
||||
imageTag.tagName = "picture";
|
||||
let html = `<source
|
||||
media="(max-width:480px)"
|
||||
srcset="${meta.webp[0].url}"
|
||||
type="image/webp"
|
||||
/>
|
||||
<source
|
||||
media="(max-width:480px)"
|
||||
srcset="${meta.jpeg[0].url}"
|
||||
/>
|
||||
`
|
||||
if (meta.webp && meta.webp[1] && meta.webp[1].url) {
|
||||
html += `<source
|
||||
media="(max-width:1920px)"
|
||||
srcset="${meta.webp[1].url}"
|
||||
type="image/webp"
|
||||
/>`
|
||||
}
|
||||
if (meta.jpeg && meta.jpeg[1] && meta.jpeg[1].url) {
|
||||
html += `<source
|
||||
media="(max-width:1920px)"
|
||||
srcset="${meta.jpeg[1].url}"
|
||||
/>`
|
||||
}
|
||||
html += `<img
|
||||
class="${cls.toString()}"
|
||||
src="${src}"
|
||||
alt="${alt}"
|
||||
width="${width}"
|
||||
/>`;
|
||||
imageTag.innerHTML = html;
|
||||
}
|
||||
|
||||
|
||||
eleventyConfig.addTransform("picture", function (str) {
|
||||
const parsed = parse(str);
|
||||
for (const imageTag of parsed.querySelectorAll(".cm-s-obsidian img")) {
|
||||
const src = imageTag.getAttribute("src");
|
||||
if (src && src.startsWith("/") && !src.endsWith(".svg")) {
|
||||
const cls = imageTag.classList.value;
|
||||
const alt = imageTag.getAttribute("alt");
|
||||
const width = imageTag.getAttribute("width") || '';
|
||||
|
||||
try {
|
||||
const meta = transformImage(
|
||||
"./src/site" + decodeURI(imageTag.getAttribute("src")),
|
||||
cls.toString(),
|
||||
alt,
|
||||
["(max-width: 480px)", "(max-width: 1024px)"]
|
||||
);
|
||||
|
||||
if (meta) {
|
||||
fillPictureSourceSets(src, cls, alt, meta, width, imageTag);
|
||||
}
|
||||
} catch {
|
||||
// Make it fault tolarent.
|
||||
}
|
||||
}
|
||||
}
|
||||
return str && parsed.innerHTML;
|
||||
});
|
||||
|
||||
eleventyConfig.addTransform("table", function (str) {
|
||||
const parsed = parse(str);
|
||||
for (const t of parsed.querySelectorAll(".cm-s-obsidian > table")) {
|
||||
let inner = t.innerHTML;
|
||||
t.tagName = "div";
|
||||
t.classList.add("table-wrapper");
|
||||
t.innerHTML = `<table>${inner}</table>`;
|
||||
}
|
||||
|
||||
for (const t of parsed.querySelectorAll(
|
||||
".cm-s-obsidian > .block-language-dataview > table"
|
||||
)) {
|
||||
t.classList.add("dataview");
|
||||
t.classList.add("table-view-table");
|
||||
t.querySelector("thead")?.classList.add("table-view-thead");
|
||||
t.querySelector("tbody")?.classList.add("table-view-tbody");
|
||||
t.querySelectorAll("thead > tr")?.forEach((tr) => {
|
||||
tr.classList.add("table-view-tr-header");
|
||||
});
|
||||
t.querySelectorAll("thead > tr > th")?.forEach((th) => {
|
||||
th.classList.add("table-view-th");
|
||||
});
|
||||
}
|
||||
return str && parsed.innerHTML;
|
||||
});
|
||||
|
||||
eleventyConfig.addTransform("htmlMinifier", (content, outputPath) => {
|
||||
if (
|
||||
process.env.NODE_ENV === "production" &&
|
||||
outputPath &&
|
||||
outputPath.endsWith(".html")
|
||||
) {
|
||||
return htmlMinifier.minify(content, {
|
||||
useShortDoctype: true,
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
keepClosingSlash: true,
|
||||
});
|
||||
}
|
||||
return content;
|
||||
});
|
||||
|
||||
eleventyConfig.addPassthroughCopy("src/site/img");
|
||||
eleventyConfig.addPassthroughCopy("src/site/scripts");
|
||||
eleventyConfig.addPassthroughCopy("src/site/styles/_theme.*.css");
|
||||
eleventyConfig.addPlugin(faviconsPlugin, { outputDir: "dist" });
|
||||
eleventyConfig.addPlugin(tocPlugin, {
|
||||
ul: true,
|
||||
tags: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
||||
});
|
||||
|
||||
|
||||
eleventyConfig.addFilter("dateToZulu", function (date) {
|
||||
if (!date) return "";
|
||||
return new Date(date).toISOString("dd-MM-yyyyTHH:mm:ssZ");
|
||||
});
|
||||
eleventyConfig.addFilter("jsonify", function (variable) {
|
||||
return JSON.stringify(variable) || '""';
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter("validJson", function (variable) {
|
||||
if (Array.isArray(variable)) {
|
||||
return variable.map((x) => x.replaceAll("\\", "\\\\")).join(",");
|
||||
} else if (typeof variable === "string") {
|
||||
return variable.replaceAll("\\", "\\\\");
|
||||
}
|
||||
return variable;
|
||||
});
|
||||
|
||||
eleventyConfig.addPlugin(pluginRss, {
|
||||
posthtmlRenderOptions: {
|
||||
closingSingleTag: "slash",
|
||||
singleTags: ["link"],
|
||||
},
|
||||
});
|
||||
|
||||
userEleventySetup(eleventyConfig);
|
||||
|
||||
return {
|
||||
dir: {
|
||||
input: "src/site",
|
||||
output: "dist",
|
||||
data: `_data`,
|
||||
},
|
||||
templateFormats: ["njk", "md", "11ty.js"],
|
||||
htmlTemplateEngine: "njk",
|
||||
markdownTemplateEngine: "njk",
|
||||
passthroughFileCopy: true,
|
||||
};
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
netlify/functions
|
|
@ -0,0 +1,4 @@
|
|||
#THEME=https://raw.githubusercontent.com/colineckert/obsidian-things/main/obsidian.css
|
||||
#THEME=https://github.com/kepano/obsidian-minimal/blob/master/obsidian.css
|
||||
#BASE_THEME=light
|
||||
dgHomeLink=true
|
|
@ -0,0 +1,15 @@
|
|||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
ignore:
|
||||
- dependency-name: "@sindresorhus/slugify"
|
||||
# For slugify, ignore all updates.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
node_modules
|
||||
dist
|
||||
netlify/functions/search/data.json
|
||||
netlify/functions/search/index.json
|
||||
src/site/styles/theme.*.css
|
||||
src/site/styles/_theme.*.css
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
.idea/
|
||||
.vercel
|
||||
.cache
|
||||
_site/
|
||||
**/.DS_Store
|
|
@ -0,0 +1,9 @@
|
|||
# Digital Obsidian Garden
|
||||
This is the template to be used together with the [Digital Garden Obsidian Plugin](https://github.com/oleeskild/Obsidian-Digital-Garden).
|
||||
See the README in the plugin repo for information on how to set it up.
|
||||
|
||||
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/oleeskild/digitalgarden)
|
||||
|
||||
---
|
||||
## Docs
|
||||
Docs are available at [dg-docs.ole.dev](https://dg-docs.ole.dev/)
|
|
@ -0,0 +1,14 @@
|
|||
[build]
|
||||
publish = "dist"
|
||||
command = "npm install && npm run build"
|
||||
|
||||
[[redirects]]
|
||||
from = "/api/*"
|
||||
to = "/.netlify/functions/:splat"
|
||||
status = 200
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/404"
|
||||
status = 404
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"name": "web",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "npm-run-all get-theme build:sass --parallel watch:*",
|
||||
"watch:sass": "sass --watch src/site/styles:dist/styles",
|
||||
"watch:eleventy": "cross-env ELEVENTY_ENV=dev eleventy --serve",
|
||||
"build:eleventy": "cross-env ELEVENTY_ENV=prod NODE_OPTIONS=--max-old-space-size=4096 eleventy",
|
||||
"build:sass": "sass src/site/styles:dist/styles --style compressed",
|
||||
"get-theme": "node src/site/get-theme.js",
|
||||
"build": "npm-run-all get-theme build:*"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "^2.0.1",
|
||||
"@11ty/eleventy-plugin-rss": "^1.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"html-minifier": "^4.0.0",
|
||||
"node-html-parser": "^6.1.4",
|
||||
"sass": "^1.49.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@11ty/eleventy-img": "^3.0.0",
|
||||
"@sindresorhus/slugify": "^1.1.0",
|
||||
"axios": "^1.2.2",
|
||||
"dotenv": "^16.0.3",
|
||||
"eleventy-plugin-gen-favicons": "^1.1.2",
|
||||
"eleventy-plugin-nesting-toc": "^1.3.0",
|
||||
"fs-file-tree": "^1.1.1",
|
||||
"glob": "^10.2.1",
|
||||
"gray-matter": "^4.0.3",
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-anchor": "^8.6.7",
|
||||
"markdown-it-attrs": "^4.1.6",
|
||||
"markdown-it-footnote": "^3.0.3",
|
||||
"markdown-it-mark": "^3.0.1",
|
||||
"markdown-it-mathjax3": "^4.3.1",
|
||||
"markdown-it-plantuml": "^1.4.1",
|
||||
"markdown-it-task-checkbox": "^1.0.6",
|
||||
"npm-run-all": "^4.1.5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"filesToDelete": [
|
||||
"src/site/styles/style.css",
|
||||
"src/site/index.njk",
|
||||
"src/site/index.11tydata.js",
|
||||
"src/site/_data/filetree.js",
|
||||
"api/search.js",
|
||||
"netlify/functions/search/search.js",
|
||||
"src/site/versionednote.njk",
|
||||
"src/site/_includes/layouts/versionednote.njk",
|
||||
"src/site/lunr-index.js",
|
||||
"src/site/_data/versionednotes.js",
|
||||
"src/site/lunr.njk"
|
||||
|
||||
],
|
||||
"filesToAdd": [
|
||||
"src/site/styles/custom-style.scss",
|
||||
".env",
|
||||
"src/site/favicon.svg",
|
||||
"src/site/img/default-note-icon.svg",
|
||||
"src/site/img/tree-1.svg",
|
||||
"src/site/img/tree-2.svg",
|
||||
"src/site/img/tree-3.svg",
|
||||
"src/helpers/userUtils.js",
|
||||
"src/helpers/userSetup.js",
|
||||
"vercel.json",
|
||||
"netlify.toml"
|
||||
],
|
||||
"filesToModify": [
|
||||
".eleventy.js",
|
||||
".eleventyignore",
|
||||
"README.md",
|
||||
"package-lock.json",
|
||||
"package.json",
|
||||
"src/site/404.njk",
|
||||
"src/site/sitemap.njk",
|
||||
"src/site/feed.njk",
|
||||
"src/site/styles/style.scss",
|
||||
"src/site/styles/digital-garden-base.scss",
|
||||
"src/site/styles/obsidian-base.scss",
|
||||
"src/site/notes/notes.json",
|
||||
"src/site/notes/notes.11tydata.js",
|
||||
"src/site/_includes/layouts/note.njk",
|
||||
"src/site/_includes/layouts/index.njk",
|
||||
"src/site/_includes/components/notegrowthhistory.njk",
|
||||
"src/site/_includes/components/pageheader.njk",
|
||||
"src/site/_includes/components/linkPreview.njk",
|
||||
"src/site/_includes/components/references.njk",
|
||||
"src/site/_includes/components/sidebar.njk",
|
||||
"src/site/_includes/components/graphScript.njk",
|
||||
"src/site/_includes/components/filetree.njk",
|
||||
"src/site/_includes/components/filetreeNavbar.njk",
|
||||
"src/site/_includes/components/navbar.njk",
|
||||
"src/site/_includes/components/searchButton.njk",
|
||||
"src/site/_includes/components/searchContainer.njk",
|
||||
"src/site/_includes/components/searchScript.njk",
|
||||
"src/site/_includes/components/calloutScript.njk",
|
||||
"src/site/_includes/components/lucideIcons.njk",
|
||||
"src/site/_includes/components/timestamps.njk",
|
||||
"src/site/_data/meta.js",
|
||||
"src/site/_data/dynamics.js",
|
||||
"src/site/img/outgoing.svg",
|
||||
"src/helpers/constants.js",
|
||||
"src/helpers/utils.js",
|
||||
"src/helpers/filetreeUtils.js",
|
||||
"src/helpers/linkUtils.js",
|
||||
"src/site/get-theme.js",
|
||||
"src/site/_data/eleventyComputed.js",
|
||||
"src/site/graph.njk",
|
||||
"src/site/search-index.njk"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
exports.ALL_NOTE_SETTINGS= [
|
||||
"dgHomeLink",
|
||||
"dgPassFrontmatter",
|
||||
"dgShowBacklinks",
|
||||
"dgShowLocalGraph",
|
||||
"dgShowInlineTitle",
|
||||
"dgShowFileTree",
|
||||
"dgEnableSearch",
|
||||
"dgShowToc",
|
||||
"dgLinkPreview",
|
||||
"dgShowTags"
|
||||
];
|
|
@ -0,0 +1,114 @@
|
|||
const sortTree = (unsorted) => {
|
||||
//Sort by folder before file, then by name
|
||||
const orderedTree = Object.keys(unsorted)
|
||||
.sort((a, b) => {
|
||||
|
||||
let a_pinned = unsorted[a].pinned || false;
|
||||
let b_pinned = unsorted[b].pinned || false;
|
||||
if (a_pinned != b_pinned) {
|
||||
if (a_pinned) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
const a_is_note = a.indexOf(".md") > -1;
|
||||
const b_is_note = b.indexOf(".md") > -1;
|
||||
|
||||
if (a_is_note && !b_is_note) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!a_is_note && b_is_note) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a.toLowerCase() > b.toLowerCase()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
})
|
||||
.reduce((obj, key) => {
|
||||
obj[key] = unsorted[key];
|
||||
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
for (const key of Object.keys(orderedTree)) {
|
||||
if (orderedTree[key].isFolder) {
|
||||
orderedTree[key] = sortTree(orderedTree[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return orderedTree;
|
||||
};
|
||||
|
||||
function getPermalinkMeta(note, key) {
|
||||
let permalink = "/";
|
||||
let parts = note.filePathStem.split("/");
|
||||
let name = parts[parts.length - 1];
|
||||
let noteIcon = process.env.NOTE_ICON_DEFAULT;
|
||||
let hide = false;
|
||||
let pinned = false;
|
||||
let folders = null;
|
||||
try {
|
||||
if (note.data.permalink) {
|
||||
permalink = note.data.permalink;
|
||||
}
|
||||
if (note.data.tags && note.data.tags.indexOf("gardenEntry") != -1) {
|
||||
permalink = "/";
|
||||
}
|
||||
if (note.data.title) {
|
||||
name = note.data.title;
|
||||
}
|
||||
if (note.data.noteIcon) {
|
||||
noteIcon = note.data.noteIcon;
|
||||
}
|
||||
// Reason for adding the hide flag instead of removing completely from file tree is to
|
||||
// allow users to use the filetree data elsewhere without the fear of losing any data.
|
||||
if (note.data.hide) {
|
||||
hide = note.data.hide;
|
||||
}
|
||||
if (note.data.pinned) {
|
||||
pinned = note.data.pinned;
|
||||
}
|
||||
if (note.data["dg-path"]) {
|
||||
folders = note.data["dg-path"].split("/");
|
||||
} else {
|
||||
folders = note.filePathStem
|
||||
.split("notes/")[1]
|
||||
.split("/");
|
||||
}
|
||||
folders[folders.length - 1]+= ".md";
|
||||
} catch {
|
||||
//ignore
|
||||
}
|
||||
|
||||
return [{ permalink, name, noteIcon, hide, pinned }, folders];
|
||||
}
|
||||
|
||||
function assignNested(obj, keyPath, value) {
|
||||
lastKeyIndex = keyPath.length - 1;
|
||||
for (var i = 0; i < lastKeyIndex; ++i) {
|
||||
key = keyPath[i];
|
||||
if (!(key in obj)) {
|
||||
obj[key] = { isFolder: true };
|
||||
}
|
||||
obj = obj[key];
|
||||
}
|
||||
obj[keyPath[lastKeyIndex]] = value;
|
||||
}
|
||||
|
||||
function getFileTree(data) {
|
||||
const tree = {};
|
||||
(data.collections.note || []).forEach((note) => {
|
||||
const [meta, folders] = getPermalinkMeta(note);
|
||||
assignNested(tree, folders, { isNote: true, ...meta });
|
||||
});
|
||||
const fileTree = sortTree(tree);
|
||||
return fileTree;
|
||||
}
|
||||
|
||||
exports.getFileTree = getFileTree;
|
|
@ -0,0 +1,100 @@
|
|||
const wikiLinkRegex = /\[\[(.*?\|.*?)\]\]/g;
|
||||
const internalLinkRegex = /href="\/(.*?)"/g;
|
||||
|
||||
function caselessCompare(a, b) {
|
||||
return a.toLowerCase() === b.toLowerCase();
|
||||
}
|
||||
|
||||
function extractLinks(content) {
|
||||
return [
|
||||
...(content.match(wikiLinkRegex) || []).map(
|
||||
(link) =>
|
||||
link
|
||||
.slice(2, -2)
|
||||
.split("|")[0]
|
||||
.replace(/.(md|markdown)\s?$/i, "")
|
||||
.replace("\\", "")
|
||||
.trim()
|
||||
.split("#")[0]
|
||||
),
|
||||
...(content.match(internalLinkRegex) || []).map(
|
||||
(link) =>
|
||||
link
|
||||
.slice(6, -1)
|
||||
.split("|")[0]
|
||||
.replace(/.(md|markdown)\s?$/i, "")
|
||||
.replace("\\", "")
|
||||
.trim()
|
||||
.split("#")[0]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
function getGraph(data) {
|
||||
let nodes = {};
|
||||
let links = [];
|
||||
let stemURLs = {};
|
||||
let homeAlias = "/";
|
||||
(data.collections.note || []).forEach((v, idx) => {
|
||||
let fpath = v.filePathStem.replace("/notes/", "");
|
||||
let parts = fpath.split("/");
|
||||
let group = "none";
|
||||
if (parts.length >= 3) {
|
||||
group = parts[parts.length - 2];
|
||||
}
|
||||
nodes[v.url] = {
|
||||
id: idx,
|
||||
title: v.data.title || v.fileSlug,
|
||||
url: v.url,
|
||||
group,
|
||||
home:
|
||||
v.data["dg-home"] ||
|
||||
(v.data.tags && v.data.tags.indexOf("gardenEntry") > -1) ||
|
||||
false,
|
||||
outBound: extractLinks(v.template.frontMatter.content),
|
||||
neighbors: new Set(),
|
||||
backLinks: new Set(),
|
||||
noteIcon: v.data.noteIcon || process.env.NOTE_ICON_DEFAULT,
|
||||
hide: v.data.hideInGraph || false,
|
||||
};
|
||||
stemURLs[fpath] = v.url;
|
||||
if (
|
||||
v.data["dg-home"] ||
|
||||
(v.data.tags && v.data.tags.indexOf("gardenEntry") > -1)
|
||||
) {
|
||||
homeAlias = v.url;
|
||||
}
|
||||
});
|
||||
Object.values(nodes).forEach((node) => {
|
||||
let outBound = new Set();
|
||||
node.outBound.forEach((olink) => {
|
||||
let link = (stemURLs[olink] || olink).split("#")[0];
|
||||
outBound.add(link);
|
||||
});
|
||||
node.outBound = Array.from(outBound);
|
||||
node.outBound.forEach((link) => {
|
||||
let n = nodes[link];
|
||||
if (n) {
|
||||
n.neighbors.add(node.url);
|
||||
n.backLinks.add(node.url);
|
||||
node.neighbors.add(n.url);
|
||||
links.push({ source: node.id, target: n.id });
|
||||
}
|
||||
});
|
||||
});
|
||||
Object.keys(nodes).map((k) => {
|
||||
nodes[k].neighbors = Array.from(nodes[k].neighbors);
|
||||
nodes[k].backLinks = Array.from(nodes[k].backLinks);
|
||||
nodes[k].size = nodes[k].neighbors.length;
|
||||
});
|
||||
return {
|
||||
homeAlias,
|
||||
nodes,
|
||||
links,
|
||||
};
|
||||
}
|
||||
|
||||
exports.wikiLinkRegex = wikiLinkRegex;
|
||||
exports.internalLinkRegex = internalLinkRegex;
|
||||
exports.extractLinks = extractLinks;
|
||||
exports.getGraph = getGraph;
|
|
@ -0,0 +1,10 @@
|
|||
function userMarkdownSetup(md) {
|
||||
// The md parameter stands for the markdown-it instance used throughout the site generator.
|
||||
// Feel free to add any plugin you want here instead of /.eleventy.js
|
||||
}
|
||||
function userEleventySetup(eleventyConfig) {
|
||||
// The eleventyConfig parameter stands for the the config instantiated in /.eleventy.js.
|
||||
// Feel free to add any plugin you want here instead of /.eleventy.js
|
||||
}
|
||||
exports.userMarkdownSetup = userMarkdownSetup;
|
||||
exports.userEleventySetup = userEleventySetup;
|
|
@ -0,0 +1,7 @@
|
|||
// Put your computations here.
|
||||
|
||||
function userComputed(data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
exports.userComputed = userComputed;
|
|
@ -0,0 +1,51 @@
|
|||
const slugify = require("@sindresorhus/slugify");
|
||||
|
||||
function headerToId(heading) {
|
||||
var slugifiedHeader = slugify(heading);
|
||||
if(!slugifiedHeader){
|
||||
return heading;
|
||||
}
|
||||
return slugifiedHeader;
|
||||
}
|
||||
|
||||
function namedHeadings(md, state) {
|
||||
|
||||
var ids = {}
|
||||
|
||||
state.tokens.forEach(function(token, i) {
|
||||
if (token.type === 'heading_open') {
|
||||
var text = md.renderer.render(state.tokens[i + 1].children, md.options)
|
||||
var id = headerToId(text);
|
||||
var uniqId = uncollide(ids, id)
|
||||
ids[uniqId] = true
|
||||
setAttr(token, 'id', uniqId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function uncollide(ids, id) {
|
||||
if (!ids[id]) return id
|
||||
var i = 1
|
||||
while (ids[id + '-' + i]) { i++ }
|
||||
return id + '-' + i
|
||||
}
|
||||
|
||||
function setAttr(token, attr, value, options) {
|
||||
var idx = token.attrIndex(attr)
|
||||
|
||||
if (idx === -1) {
|
||||
token.attrPush([attr, value])
|
||||
} else if (options && options.append) {
|
||||
token.attrs[idx][1] =
|
||||
token.attrs[idx][1] + ' ' + value
|
||||
} else {
|
||||
token.attrs[idx][1] = value
|
||||
}
|
||||
}
|
||||
|
||||
//https://github.com/rstacruz/markdown-it-named-headings/blob/master/index.js
|
||||
exports.namedHeadingsFilter = function (md, options) {
|
||||
md.core.ruler.push('named_headings', namedHeadings.bind(null, md));
|
||||
}
|
||||
|
||||
exports.headerToId = headerToId;
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Nothing here</title>
|
||||
<link href="/styles/digital-garden-base.css" rel="stylesheet">
|
||||
{%-if meta.themeStyle%}
|
||||
<link href="/styles/obsidian-base.css" rel="stylesheet">
|
||||
<link href="{{meta.themeStyle}}" rel="stylesheet">
|
||||
{% else %}
|
||||
<link href="/styles/style.css" rel="stylesheet">
|
||||
{%endif%}
|
||||
|
||||
<link href="/styles/custom-style.css" rel="stylesheet">
|
||||
|
||||
</head>
|
||||
<body class="theme-{{meta.baseTheme}} markdown-preview-view">
|
||||
<div class="content centered">
|
||||
|
||||
{%-if not meta.themeStyle%}
|
||||
<div class="font-bg"> 😎 </div>
|
||||
{%endif%}
|
||||
<h1>There is nothing here</h1>
|
||||
<p>If you got here from a link, this note is probably not made public</p>
|
||||
<a href="/">Go back home</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,60 @@
|
|||
const fsFileTree = require("fs-file-tree");
|
||||
|
||||
const BASE_PATH = "src/site/_includes/components/user";
|
||||
const STYLE_PATH = "src/site/styles/user";
|
||||
const NAMESPACES = ["index", "notes", "common"];
|
||||
const SLOTS = ["head", "header", "beforeContent", "afterContent", "footer"];
|
||||
const FILE_TREE_NAMESPACE = "filetree";
|
||||
const FILE_TREE_SLOTS = ["beforeTitle", "afterTitle"];
|
||||
const SIDEBAR_NAMESPACE = "sidebar";
|
||||
const SIDEBAR_SLOTS = ["top", "bottom"];
|
||||
const STYLES_NAMESPACE = "styles";
|
||||
|
||||
const generateComponentPaths = async (namespace, slots) => {
|
||||
const data = {};
|
||||
for (let index = 0; index < slots.length; index++) {
|
||||
const slot = slots[index];
|
||||
try {
|
||||
const tree = await fsFileTree(`${BASE_PATH}/${namespace}/${slot}`);
|
||||
let comps = Object.keys(tree)
|
||||
.filter((p) => p.indexOf(".njk") != -1)
|
||||
.map((p) => `components/user/${namespace}/${slot}/${p}`);
|
||||
comps.sort();
|
||||
data[slot] = comps;
|
||||
} catch {
|
||||
data[slot] = [];
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const generateStylesPaths = async () => {
|
||||
try {
|
||||
const tree = await fsFileTree(`${STYLE_PATH}`);
|
||||
let comps = Object.keys(tree).map((p) =>
|
||||
`/styles/user/${p}`.replace(".scss", ".css")
|
||||
);
|
||||
comps.sort();
|
||||
return comps;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = async () => {
|
||||
const data = {};
|
||||
for (let index = 0; index < NAMESPACES.length; index++) {
|
||||
const ns = NAMESPACES[index];
|
||||
data[ns] = await generateComponentPaths(ns, SLOTS);
|
||||
}
|
||||
data[FILE_TREE_NAMESPACE] = await generateComponentPaths(
|
||||
FILE_TREE_NAMESPACE,
|
||||
FILE_TREE_SLOTS
|
||||
);
|
||||
data[SIDEBAR_NAMESPACE] = await generateComponentPaths(
|
||||
SIDEBAR_NAMESPACE,
|
||||
SIDEBAR_SLOTS
|
||||
);
|
||||
data[STYLES_NAMESPACE] = await generateStylesPaths();
|
||||
return data;
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
const { getGraph } = require("../../helpers/linkUtils");
|
||||
const { getFileTree } = require("../../helpers/filetreeUtils");
|
||||
const { userComputed } = require("../../helpers/userUtils");
|
||||
|
||||
module.exports = {
|
||||
graph: (data) => getGraph(data),
|
||||
filetree: (data) => getFileTree(data),
|
||||
userComputed: (data) => userComputed(data)
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
require("dotenv").config();
|
||||
const axios = require("axios");
|
||||
const fs = require("fs");
|
||||
const crypto = require("crypto");
|
||||
const { globSync } = require("glob");
|
||||
|
||||
module.exports = async (data) => {
|
||||
let baseUrl = process.env.SITE_BASE_URL || "";
|
||||
if (baseUrl && !baseUrl.startsWith("http")) {
|
||||
baseUrl = "https://" + baseUrl;
|
||||
}
|
||||
let themeStyle = globSync("src/site/styles/_theme.*.css")[0] || "";
|
||||
if (themeStyle) {
|
||||
themeStyle = themeStyle.split("site")[1];
|
||||
}
|
||||
let bodyClasses = [];
|
||||
let noteIconsSettings = {
|
||||
filetree: false,
|
||||
links: false,
|
||||
title: false,
|
||||
default: process.env.NOTE_ICON_DEFAULT,
|
||||
};
|
||||
|
||||
const styleSettingsCss = process.env.STYLE_SETTINGS_CSS || "";
|
||||
|
||||
if (process.env.NOTE_ICON_TITLE && process.env.NOTE_ICON_TITLE == "true") {
|
||||
bodyClasses.push("title-note-icon");
|
||||
noteIconsSettings.title = true;
|
||||
}
|
||||
if (
|
||||
process.env.NOTE_ICON_FILETREE &&
|
||||
process.env.NOTE_ICON_FILETREE == "true"
|
||||
) {
|
||||
bodyClasses.push("filetree-note-icon");
|
||||
noteIconsSettings.filetree = true;
|
||||
}
|
||||
if (
|
||||
process.env.NOTE_ICON_INTERNAL_LINKS &&
|
||||
process.env.NOTE_ICON_INTERNAL_LINKS == "true"
|
||||
) {
|
||||
bodyClasses.push("links-note-icon");
|
||||
noteIconsSettings.links = true;
|
||||
}
|
||||
if (
|
||||
process.env.NOTE_ICON_BACK_LINKS &&
|
||||
process.env.NOTE_ICON_BACK_LINKS == "true"
|
||||
) {
|
||||
bodyClasses.push("backlinks-note-icon");
|
||||
noteIconsSettings.backlinks = true;
|
||||
}
|
||||
if(styleSettingsCss){
|
||||
bodyClasses.push("css-settings-manager");
|
||||
}
|
||||
|
||||
let timestampSettings = {
|
||||
timestampFormat: process.env.TIMESTAMP_FORMAT || "MMM dd, yyyy h:mm a",
|
||||
showCreated: process.env.SHOW_CREATED_TIMESTAMP == "true",
|
||||
showUpdated: process.env.SHOW_UPDATED_TIMESTAMP == "true",
|
||||
};
|
||||
const meta = {
|
||||
env: process.env.ELEVENTY_ENV,
|
||||
theme: process.env.THEME,
|
||||
themeStyle,
|
||||
bodyClasses: bodyClasses.join(" "),
|
||||
noteIconsSettings,
|
||||
timestampSettings,
|
||||
baseTheme: process.env.BASE_THEME || "dark",
|
||||
siteName: process.env.SITE_NAME_HEADER || "Digital Garden",
|
||||
siteBaseUrl: baseUrl,
|
||||
styleSettingsCss,
|
||||
buildDate: new Date(),
|
||||
};
|
||||
|
||||
return meta;
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
<script src="https://cdn.jsdelivr.net/npm/lucide@0.115.0/dist/umd/lucide.min.js"></script>
|
||||
<script>
|
||||
// Create callout icons
|
||||
window.addEventListener("load", () => {
|
||||
document.querySelectorAll(".callout").forEach((elem) => {
|
||||
const icon = getComputedStyle(elem).getPropertyValue('--callout-icon');
|
||||
const iconName = icon && icon.trim().replace(/^lucide-/, "");
|
||||
|
||||
if (iconName) {
|
||||
const calloutTitle = elem.querySelector(".callout-title");
|
||||
|
||||
if (calloutTitle) {
|
||||
const calloutIconContainer = document.createElement("div");
|
||||
const calloutIcon = document.createElement("i");
|
||||
|
||||
calloutIconContainer.appendChild(calloutIcon);
|
||||
calloutIcon.setAttribute("icon-name", iconName);
|
||||
calloutIconContainer.setAttribute("class", "callout-icon");
|
||||
calloutTitle.insertBefore(calloutIconContainer, calloutTitle.firstChild);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
lucide.createIcons();
|
||||
|
||||
// Collapse callouts
|
||||
Array.from(document.querySelectorAll(".callout.is-collapsible")).forEach((elem) => {
|
||||
elem.querySelector('.callout-title').addEventListener("click", (event) => {
|
||||
if (elem.classList.contains("is-collapsed")) {
|
||||
elem.classList.remove("is-collapsed");
|
||||
} else {
|
||||
elem.classList.add("is-collapsed");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,56 @@
|
|||
{% macro menuItem(fileOrFolderName, fileOrFolder, step, currentPath) %}
|
||||
{%if fileOrFolder.isNote or fileOrFolder.isFolder%}
|
||||
<div x-show="isOpen" style="display:none" class="{{'filelist' if step>0}}">
|
||||
{%if fileOrFolder.isNote and not fileOrFolder.hide %}
|
||||
<div @click.stop class="notelink {{ 'active-note' if fileOrFolder.permalink === permalink}}">
|
||||
{%- if not meta.noteIconsSettings.filetree -%}<i icon-name="sticky-note" aria-hidden="true"></i>{%- endif -%}
|
||||
<a data-note-icon="{{fileOrFolder.noteIcon}}" style="text-decoration: none;" class="filename" href="{{fileOrFolder.permalink}}">{{fileOrFolder.name}} </a>
|
||||
</div>
|
||||
{% elif fileOrFolder.isFolder%}
|
||||
<div class="folder inner-folder" x-data="{isOpen: $persist(false).as('{{currentPath}}')}" @click.stop="isOpen=!isOpen">
|
||||
<div class="foldername-wrapper align-icon">
|
||||
<i x-show="isOpen" style="display: none;" icon-name="chevron-down"></i>
|
||||
<i x-show="!isOpen" icon-name="chevron-right"></i>
|
||||
<span class="foldername">{{fileOrFolderName}}</span>
|
||||
</div>
|
||||
{% for fileOrFolderName, child in fileOrFolder %}
|
||||
{{menuItem(fileOrFolderName, child, step+1, (currentPath+"/"+fileOrFolderName))}}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%endif%}
|
||||
{% endmacro %}
|
||||
|
||||
<div x-init="isDesktop = (window.innerWidth>=1400) ? true: false;"
|
||||
x-on:resize.window="isDesktop = (window.innerWidth>=1400) ? true : false;"
|
||||
x-data="{isDesktop: true, showFilesMobile: false}">
|
||||
|
||||
<div x-show.important="!isDesktop" style="display: none;">
|
||||
{%include "components/filetreeNavbar.njk"%}
|
||||
</div>
|
||||
|
||||
<div x-show="showFilesMobile && !isDesktop" @click="showFilesMobile = false" style="display:none;" class="fullpage-overlay"></div>
|
||||
|
||||
<nav class="filetree-sidebar" x-show.important="isDesktop || showFilesMobile" style="display: none;">
|
||||
{% for imp in dynamics.filetree.beforeTitle %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
<a href="/" style="text-decoration: none;">
|
||||
<h1 style="text-align:center;">{{meta.siteName}}</h1>
|
||||
</a>
|
||||
{% for imp in dynamics.filetree.afterTitle %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% if settings.dgEnableSearch === true%}
|
||||
<div style="display: flex; justify-content: center;">
|
||||
{% include "components/searchButton.njk" %}
|
||||
</div>
|
||||
{%endif%}
|
||||
<div class="folder" x-data="{isOpen: true}">
|
||||
{%- for fileOrFolderName, fileOrFolder in filetree -%}
|
||||
{{menuItem(fileOrFolderName, fileOrFolder, 0, fileOrFolderName)}}
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
<nav class="navbar">
|
||||
<div class="navbar-inner">
|
||||
<span style="font-size: 1.5rem; margin-right: 10px;" @click="showFilesMobile=!showFilesMobile"><i icon-name="menu"></i></span>
|
||||
{% for imp in dynamics.filetree.beforeTitle %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
<a href="/" style="text-decoration: none;">
|
||||
<h1 style="margin: 15px !important;">{{meta.siteName}}</h1>
|
||||
</a>
|
||||
{% for imp in dynamics.filetree.afterTitle %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if settings.dgEnableSearch === true%}
|
||||
{% include "components/searchButton.njk" %}
|
||||
{%endif%}
|
||||
</nav>
|
|
@ -0,0 +1,214 @@
|
|||
<script>
|
||||
async function fetchGraphData() {
|
||||
const graphData = await fetch('/graph.json').then(res => res.json());
|
||||
const fullGraphData = filterFullGraphData(graphData);
|
||||
return {graphData, fullGraphData}
|
||||
}
|
||||
|
||||
function getNextLevelNeighbours(existing, remaining) {
|
||||
const keys = Object.values(existing).map((n) => n.neighbors).flat();
|
||||
const n_remaining = Object.keys(remaining).reduce((acc, key) => {
|
||||
if (keys.indexOf(key) != -1) {
|
||||
if (!remaining[key].hide) {
|
||||
existing[key] = remaining[key];
|
||||
}
|
||||
} else {
|
||||
acc[key] = remaining[key];
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
return existing, n_remaining;
|
||||
}
|
||||
|
||||
function filterLocalGraphData(graphData, depth) {
|
||||
if (graphData == null) {
|
||||
return null;
|
||||
}
|
||||
let remaining = JSON.parse(JSON.stringify(graphData.nodes));
|
||||
let links = JSON.parse(JSON.stringify(graphData.links));
|
||||
let currentLink = decodeURI(window.location.pathname);
|
||||
let currentNode = remaining[currentLink] || Object.values(remaining).find((v) => v.home);
|
||||
delete remaining[currentNode.url];
|
||||
if (!currentNode.home) {
|
||||
let home = Object.values(remaining).find((v) => v.home);
|
||||
delete remaining[home.url];
|
||||
}
|
||||
currentNode.current = true;
|
||||
let existing = {};
|
||||
existing[currentNode.url] = currentNode;
|
||||
for (let i = 0; i < depth; i++) {
|
||||
existing, remaining = getNextLevelNeighbours(existing, remaining);
|
||||
}
|
||||
nodes = Object.values(existing);
|
||||
if (!currentNode.home) {
|
||||
nodes = nodes.filter(n => !n.home);
|
||||
}
|
||||
let ids = nodes.map((n) => n.id);
|
||||
return {
|
||||
nodes,
|
||||
links: links.filter(function (con) {
|
||||
return ids.indexOf(con.target) > -1 && ids.indexOf(con.source) > -1;
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function getCssVar(variable) {return getComputedStyle(document.body).getPropertyValue(variable)}
|
||||
|
||||
function htmlDecode(input) {
|
||||
var doc = new DOMParser().parseFromString(input, "text/html");
|
||||
return doc.documentElement.textContent;
|
||||
}
|
||||
|
||||
function renderGraph(graphData, id, delay, fullScreen) {
|
||||
if (graphData == null) {
|
||||
return;
|
||||
}
|
||||
const el = document.getElementById(id);
|
||||
width = el.offsetWidth;
|
||||
height = el.offsetHeight;
|
||||
const highlightNodes = new Set();
|
||||
let hoverNode = null;
|
||||
const color = getCssVar("--graph-main");
|
||||
const mutedColor = getCssVar("--graph-muted");
|
||||
let Graph = ForceGraph()
|
||||
(el)
|
||||
.graphData(graphData)
|
||||
.nodeId('id')
|
||||
.nodeLabel('title')
|
||||
.linkSource('source')
|
||||
.linkTarget('target')
|
||||
.d3AlphaDecay(0.10)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.linkDirectionalArrowLength(2)
|
||||
.linkDirectionalArrowRelPos(0.5)
|
||||
.autoPauseRedraw(false)
|
||||
.linkColor((link) => {
|
||||
if (hoverNode == null) {
|
||||
return color;
|
||||
}
|
||||
if (link.source.id == hoverNode.id || link.target.id == hoverNode.id) {
|
||||
return color;
|
||||
} else {
|
||||
return mutedColor;
|
||||
}
|
||||
|
||||
})
|
||||
.nodeCanvasObject((node, ctx) => {
|
||||
const numberOfNeighbours = (node.neighbors && node.neighbors.length) || 2;
|
||||
const nodeR = Math.min(7, Math.max(numberOfNeighbours / 2, 2));
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, nodeR, 0, 2 * Math.PI, false);
|
||||
if (hoverNode == null) {
|
||||
ctx.fillStyle = color;
|
||||
} else {
|
||||
if (node == hoverNode || highlightNodes.has(node.url)) {
|
||||
ctx.fillStyle = color;
|
||||
} else {
|
||||
ctx.fillStyle = mutedColor;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
|
||||
if (node.current) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, nodeR + 1, 0, 2 * Math.PI, false);
|
||||
ctx.lineWidth = 0.5;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
const label = htmlDecode(node.title)
|
||||
const fontSize = 3.5;
|
||||
ctx.font = `${fontSize}px Sans-Serif`;
|
||||
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillText(label, node.x, node.y + nodeR + 2);
|
||||
})
|
||||
.onNodeClick(node => {
|
||||
window.location = node.url;
|
||||
})
|
||||
.onNodeHover(node => {
|
||||
highlightNodes.clear();
|
||||
if (node) {
|
||||
highlightNodes.add(node);
|
||||
node.neighbors.forEach(neighbor => highlightNodes.add(neighbor));
|
||||
}
|
||||
hoverNode = node || null;
|
||||
|
||||
});
|
||||
if (fullScreen || (delay != null && graphData.nodes.length > 4)) {
|
||||
setTimeout(() => {
|
||||
Graph.zoomToFit(5, 75);
|
||||
}, delay || 200);
|
||||
}
|
||||
return Graph;
|
||||
}
|
||||
|
||||
function renderLocalGraph(graphData, depth, fullScreen) {
|
||||
if (window.graph){
|
||||
window.graph._destructor();
|
||||
}
|
||||
const data = filterLocalGraphData(graphData, depth);
|
||||
return renderGraph(data, 'link-graph', null, fullScreen);
|
||||
}
|
||||
|
||||
function filterFullGraphData(graphData) {
|
||||
if (graphData == null) {
|
||||
return null;
|
||||
}
|
||||
graphData = JSON.parse(JSON.stringify(graphData));
|
||||
const hiddens = Object.values(graphData.nodes).filter((n) => n.hide).map((n) => n.id);
|
||||
const data = {
|
||||
links: JSON.parse(JSON.stringify(graphData.links)).filter((l) => hiddens.indexOf(l.source) == -1 && hiddens.indexOf(l.target) == -1),
|
||||
nodes: [...Object.values(graphData.nodes).filter((n) => !n.hide)]
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function openFullGraph(fullGraphData) {
|
||||
lucide.createIcons({
|
||||
attrs: {
|
||||
class: ["svg-icon"]
|
||||
}
|
||||
});
|
||||
return renderGraph(fullGraphData, "full-graph-container", 200, false);;
|
||||
}
|
||||
|
||||
function closefullGraph(fullGraph) {
|
||||
if (fullGraph) {
|
||||
fullGraph._destructor();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
</script>
|
||||
<div x-init="{graphData, fullGraphData} = await fetchGraphData();" x-data="{ graphData: null, depth: 1, graph: null, fullGraph: null, showFullGraph: false, fullScreen: false, fullGraphData: null}" id="graph-component" x-bind:class="fullScreen ? 'graph graph-fs' : 'graph'" v-scope>
|
||||
<div class="graph-title-container">
|
||||
<div class="graph-title">Connected Pages</div>
|
||||
<div id="graph-controls">
|
||||
<div class="depth-control">
|
||||
<label for="graph-depth">Depth</label>
|
||||
<div class="slider">
|
||||
<input x-model.number="depth" name="graph-depth" list="depthmarkers" type="range" step="1" min="1" max="3" id="graph-depth"/>
|
||||
<datalist id="depthmarkers">
|
||||
<option value="1" label="1"></option>
|
||||
<option value="2" label="2"></option>
|
||||
<option value="3" label="3"></option>
|
||||
</datalist>
|
||||
</div>
|
||||
<span id="depth-display" x-text="depth"></span>
|
||||
</div>
|
||||
<div class="ctrl-right">
|
||||
<span id="global-graph-btn" x-on:click="showFullGraph = true; setTimeout(() => {fullGraph = openFullGraph(fullGraphData)}, 100)"><i icon-name="globe" aria-hidden="true"></i></span>
|
||||
<span id="graph-fs-btn" x-on:click="fullScreen = !fullScreen"><i icon-name="expand" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div x-effect="window.graph = renderLocalGraph(graphData, depth, fullScreen)" id="link-graph" ></div>
|
||||
<div x-show="showFullGraph" id="full-graph" class="show" style="display: none;">
|
||||
<span id="full-graph-close" x-on:click="fullGraph = closefullGraph(fullGraph); showFullGraph = false;"><i icon-name="x" aria-hidden="true"></i></span><div id="full-graph-container"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,156 @@
|
|||
<!-- Credit for the link preview implementation goes to https://github.com/maximevaillancourt/digital-garden-jekyll-template/blob/master/_includes/link-previews.html -->
|
||||
<style>
|
||||
#tooltip-wrapper {
|
||||
background: var(--background-primary);
|
||||
padding: 1em;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
width: 80%;
|
||||
max-width: 400px;
|
||||
height: auto;
|
||||
max-height: 300px;
|
||||
font-size: 0.8em;
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,0.1);
|
||||
opacity: 0;
|
||||
transition: opacity 100ms;
|
||||
unicode-bidi: plaintext;
|
||||
overflow-y: scroll;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#tooltip-wrapper:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
width: 100%;
|
||||
unicode-bidi: plaintext;
|
||||
height: 75px;
|
||||
}
|
||||
</style>
|
||||
<div style="opacity: 0; display: none;" id='tooltip-wrapper'>
|
||||
<div id='tooltip-content'>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<iframe style="display: none; height: 0; width: 0;" id='link-preview-iframe' src="">
|
||||
</iframe>
|
||||
|
||||
<script>
|
||||
var opacityTimeout;
|
||||
var contentTimeout;
|
||||
var transitionDurationMs = 100;
|
||||
|
||||
var iframe = document.getElementById('link-preview-iframe')
|
||||
var tooltipWrapper = document.getElementById('tooltip-wrapper')
|
||||
var tooltipContent = document.getElementById('tooltip-content')
|
||||
|
||||
var linkHistories = {};
|
||||
|
||||
function hideTooltip() {
|
||||
opacityTimeout = setTimeout(function () {
|
||||
tooltipWrapper.style.opacity = 0;
|
||||
contentTimeout = setTimeout(function () {
|
||||
tooltipContent.innerHTML = '';
|
||||
tooltipWrapper.style.display = 'none';
|
||||
}, transitionDurationMs + 1);
|
||||
}, transitionDurationMs)
|
||||
}
|
||||
|
||||
function showTooltip(event) {
|
||||
var elem = event.target;
|
||||
var elem_props = elem.getClientRects()[elem.getClientRects().length - 1];
|
||||
var top = window.pageYOffset || document.documentElement.scrollTop;
|
||||
var url = event.target.getAttribute("href");
|
||||
if (url.indexOf("http") === -1 || url.indexOf(window.location.host) !== -1) {
|
||||
let contentURL = url.split('#')[0]
|
||||
if (!linkHistories[contentURL]) {
|
||||
iframe.src = contentURL
|
||||
iframe.onload = function () {
|
||||
tooltipContentHtml = ''
|
||||
tooltipContentHtml += '<div style="font-weight: bold; unicode-bidi: plaintext;">' + iframe.contentWindow.document.querySelector('h1').innerHTML + '</div>'
|
||||
tooltipContentHtml += iframe.contentWindow.document.querySelector('.content').innerHTML
|
||||
tooltipContent.innerHTML = tooltipContentHtml
|
||||
linkHistories[contentURL] = tooltipContentHtml
|
||||
tooltipWrapper.style.display = 'block';
|
||||
tooltipWrapper.scrollTop = 0;
|
||||
setTimeout(function () {
|
||||
tooltipWrapper.style.opacity = 1;
|
||||
if (url.indexOf("#") != -1) {
|
||||
let id = url.split('#')[1];
|
||||
const focus = tooltipWrapper.querySelector(`[id='${id}']`);
|
||||
focus.classList.add('referred');
|
||||
console.log(focus);
|
||||
focus.scrollIntoView({behavior: 'smooth'}, true)
|
||||
} else {
|
||||
tooltipWrapper.scroll(0, 0);
|
||||
}
|
||||
}, 1)
|
||||
}
|
||||
} else {
|
||||
tooltipContent.innerHTML = linkHistories[contentURL]
|
||||
tooltipWrapper.style.display = 'block';
|
||||
setTimeout(function () {
|
||||
tooltipWrapper.style.opacity = 1;
|
||||
if (url.indexOf("#") != -1) {
|
||||
let id = url.split('#')[1];
|
||||
const focus = tooltipWrapper.querySelector(`[id='${id}']`);
|
||||
focus.classList.add('referred');
|
||||
focus.scrollIntoView({behavior: 'smooth'}, true)
|
||||
} else {
|
||||
tooltipWrapper.scroll(0, 0);
|
||||
}
|
||||
}, 1)
|
||||
}
|
||||
|
||||
function getInnerWidth(elem) {
|
||||
var style = window.getComputedStyle(elem);
|
||||
return elem.offsetWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight) - parseFloat(style.borderLeft) - parseFloat(style.borderRight) - parseFloat(style.marginLeft) - parseFloat(style.marginRight);
|
||||
}
|
||||
|
||||
tooltipWrapper.style.left = elem_props.left - (tooltipWrapper.offsetWidth / 2) + (elem_props.width / 2) + "px";
|
||||
|
||||
if ((window.innerHeight - elem_props.top) < (tooltipWrapper.offsetHeight)) {
|
||||
tooltipWrapper.style.top = elem_props.top + top - tooltipWrapper.offsetHeight - 10 + "px";
|
||||
} else if ((window.innerHeight - elem_props.top) > (tooltipWrapper.offsetHeight)) {
|
||||
tooltipWrapper.style.top = elem_props.top + top + 35 + "px";
|
||||
}
|
||||
|
||||
if ((elem_props.left + (elem_props.width / 2)) < (tooltipWrapper.offsetWidth / 2)) {
|
||||
tooltipWrapper.style.left = "10px";
|
||||
} else if ((document.body.clientWidth - elem_props.left - (elem_props.width / 2)) < (tooltipWrapper.offsetWidth / 2)) {
|
||||
tooltipWrapper.style.left = document.body.clientWidth - tooltipWrapper.offsetWidth - 20 + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupListeners(linkElement) {
|
||||
linkElement.addEventListener('mouseleave', function (_event) {
|
||||
hideTooltip();
|
||||
});
|
||||
|
||||
tooltipWrapper.addEventListener('mouseleave', function (_event) {
|
||||
hideTooltip();
|
||||
});
|
||||
|
||||
linkElement.addEventListener('mouseenter', function (event) {
|
||||
clearTimeout(opacityTimeout);
|
||||
clearTimeout(contentTimeout);
|
||||
showTooltip(event);
|
||||
});
|
||||
|
||||
tooltipWrapper.addEventListener('mouseenter', function (event) {
|
||||
clearTimeout(opacityTimeout);
|
||||
clearTimeout(contentTimeout);
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("load", function(event)
|
||||
{
|
||||
document.querySelectorAll('.internal-link').forEach(setupListeners);
|
||||
document.querySelectorAll('.backlink-card a').forEach(setupListeners);
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,7 @@
|
|||
<script>
|
||||
lucide.createIcons({
|
||||
attrs: {
|
||||
class: ["svg-icon"]
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,18 @@
|
|||
{%if settings.dgHomeLink === true%}
|
||||
<nav class="navbar">
|
||||
<div class="navbar-inner">
|
||||
<a href="/" style="text-decoration: none;">
|
||||
<h1 style="margin: 15px !important;">{{meta.siteName}}</h1>
|
||||
</a>
|
||||
</div>
|
||||
{% if settings.dgEnableSearch === true%}
|
||||
{% include "components/searchButton.njk" %}
|
||||
{%endif%}
|
||||
</nav>
|
||||
{%else%}
|
||||
<div class="empty-navbar" >
|
||||
{% if settings.dgEnableSearch === true%}
|
||||
{% include "components/searchButton.njk" %}
|
||||
{%endif%}
|
||||
</div>
|
||||
{%endif%}
|
|
@ -0,0 +1,16 @@
|
|||
{%- if collections.versionednote.length > 0 -%}
|
||||
<div style="position:absolute; top:100px; right:0; padding:20px 15px 40px 15px; background-color:#2a2a2a; border-radius: 25px;">
|
||||
<!-- animate big when moving over -->
|
||||
<h2>Notegrowth</h2>
|
||||
<ul style="list-style-type: none; padding:0;">
|
||||
{%- for note in collections.versionednote -%}
|
||||
{%- if note.data.item.title === page.fileSlug -%}
|
||||
<li style="padding:10px">
|
||||
<a href="{{ note.url }}">
|
||||
{{note.data.item.date}}</a>
|
||||
</li>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
{%-endif-%}
|
|
@ -0,0 +1,49 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://fastly.jsdelivr.net/npm/mermaid@9.4.0/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/prism.min.js" integrity="sha512-hpZ5pDCF2bRCweL5WoA0/N1elet1KYL5mx3LP555Eg/0ZguaHawxNvEjF6O3rufAChs16HVNhEc6blF/rZoowQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha512-sv0slik/5O0JIPdLBCR2A3XDg/1U3WuDEheZfI/DI5n8Yqc3h5kjrnr46FGBNiUAJF7rE4LHKwQ/SoSLRKAxEA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
{%include "components/calloutScript.njk"%}
|
||||
|
||||
<script src="https://fastly.jsdelivr.net/npm/force-graph@1.43.0/dist/force-graph.min.js"></script>
|
||||
|
||||
<script defer src="https://fastly.jsdelivr.net/npm/@alpinejs/persist@3.11.1/dist/cdn.min.js"></script>
|
||||
<script src="https://fastly.jsdelivr.net/npm/alpinejs@3.11.1/dist/cdn.min.js" defer></script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism-okaidia.min.css" integrity="sha512-mIs9kKbaw6JZFfSuo+MovjU+Ntggfoj8RwAmJbVXQ5mkAX5LlgETQEweFPI18humSPHymTb5iikEOKWF7I8ncQ==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
|
||||
<script src="https://fastly.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
|
||||
<link href="/styles/digital-garden-base.css" rel="stylesheet">
|
||||
{%-if meta.themeStyle%}
|
||||
<link href="/styles/obsidian-base.css" rel="stylesheet">
|
||||
<link href="{{meta.themeStyle}}" rel="stylesheet">
|
||||
{% else %}
|
||||
<link href="/styles/style.css" rel="stylesheet">
|
||||
{%endif%}
|
||||
|
||||
<link href="/styles/custom-style.css" rel="stylesheet">
|
||||
{%- for style in dynamics.styles -%}
|
||||
<link href="{{style}}" rel="stylesheet">
|
||||
{%- endfor -%}
|
||||
|
||||
{% favicons './src/site/favicon.svg', appleIconBgColor='#123' %}
|
||||
|
||||
{% if metatags %}
|
||||
{% for name, content in metatags %}
|
||||
<meta name="{{ name }}" content="{{ content }}">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if meta.styleSettingsCss %}
|
||||
<style>
|
||||
{{ meta.styleSettingsCss | safe }}
|
||||
</style>
|
||||
{% endif %}
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,26 @@
|
|||
<script>
|
||||
if (window.location.hash) {
|
||||
document.getElementById(window.location.hash.slice(1)).classList.add('referred');
|
||||
}
|
||||
window.addEventListener('hashchange', (evt) => {
|
||||
const oldParts = evt.oldURL.split("#");
|
||||
if (oldParts[1]) {
|
||||
document.getElementById(oldParts[1]).classList.remove('referred');
|
||||
}
|
||||
const newParts = evt.newURL.split("#");
|
||||
if (newParts[1]) {
|
||||
document.getElementById(newParts[1]).classList.add('referred');
|
||||
}
|
||||
}, false);
|
||||
const url_parts = window.location.href.split("#")
|
||||
const url = url_parts[0];
|
||||
const referrence = url_parts[1];
|
||||
document.querySelectorAll(".cm-s-obsidian > *[id]").forEach(function (el) {
|
||||
el.ondblclick = function(evt) {
|
||||
const ref_url = url + '#' + evt.target.id
|
||||
navigator.clipboard.writeText(ref_url);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
|
@ -0,0 +1,11 @@
|
|||
<div class="search-button align-icon" onclick="toggleSearch()">
|
||||
<span class="search-icon">
|
||||
<i icon-name="search" ></i>
|
||||
</span>
|
||||
<span class="search-text">
|
||||
<span>Search</span>
|
||||
<span style="font-size: 0.6rem; padding: 2px 2px 0 6px; text-align:center; transform: translateY(4px);" class="search-keys">
|
||||
CTRL + K
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
<div class="search-container" id="globalsearch" onclick="toggleSearch()">
|
||||
<div class="search-box">
|
||||
<input type="search" id="term" placeholder="Start typing...">
|
||||
<div id="search-results"></div>
|
||||
<footer class="search-box-footer">
|
||||
<div class="navigation-hint">
|
||||
<span>Enter to select</span>
|
||||
</div>
|
||||
|
||||
<div class="navigation-hint align-icon">
|
||||
<i icon-name="arrow-up" aria-hidden="true"></i>
|
||||
<i icon-name="arrow-down" aria-hidden="true"></i>
|
||||
<span>to navigate</span>
|
||||
</div>
|
||||
|
||||
<div class="navigation-hint">
|
||||
<span>ESC to close</span>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
{%include "components/searchScript.njk"%}
|
|
@ -0,0 +1,380 @@
|
|||
<script src="https://cdn.jsdelivr.net/npm/flexsearch@0.7.21/dist/flexsearch.bundle.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', init, false);
|
||||
document.addEventListener('DOMContentLoaded', setCorrectShortcut, false);
|
||||
|
||||
window.toggleSearch = function () {
|
||||
if (document.getElementById('globalsearch').classList.contains('active')) {
|
||||
document
|
||||
.getElementById('globalsearch')
|
||||
.classList
|
||||
.remove('active');
|
||||
} else {
|
||||
document
|
||||
.getElementById('globalsearch')
|
||||
.classList
|
||||
.add('active');
|
||||
document
|
||||
.getElementById('term')
|
||||
.focus();
|
||||
}
|
||||
}
|
||||
|
||||
window.toggleTagSearch = function (evt) {
|
||||
console.log(evt.textContent);
|
||||
const term = evt.textContent;
|
||||
if (term) {
|
||||
window
|
||||
.document
|
||||
.getElementById('term')
|
||||
.value = term.trim();
|
||||
window.toggleSearch();
|
||||
window.search();
|
||||
}
|
||||
}
|
||||
|
||||
const loadingSvg = `
|
||||
<svg width="100" height="100" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
|
||||
<g fill="none" fill-rule="evenodd" transform="translate(1 1)" stroke-width="2">
|
||||
<circle cx="22" cy="22" r="6" stroke-opacity="0">
|
||||
<animate attributeName="r"
|
||||
begin="1.5s" dur="3s"
|
||||
values="6;22"
|
||||
calcMode="linear"
|
||||
repeatCount="indefinite" />
|
||||
<animate attributeName="stroke-opacity"
|
||||
begin="1.5s" dur="3s"
|
||||
values="1;0" calcMode="linear"
|
||||
repeatCount="indefinite" />
|
||||
<animate attributeName="stroke-width"
|
||||
begin="1.5s" dur="3s"
|
||||
values="2;0" calcMode="linear"
|
||||
repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="22" cy="22" r="6" stroke-opacity="0">
|
||||
<animate attributeName="r"
|
||||
begin="3s" dur="3s"
|
||||
values="6;22"
|
||||
calcMode="linear"
|
||||
repeatCount="indefinite" />
|
||||
<animate attributeName="stroke-opacity"
|
||||
begin="3s" dur="3s"
|
||||
values="1;0" calcMode="linear"
|
||||
repeatCount="indefinite" />
|
||||
<animate attributeName="stroke-width"
|
||||
begin="3s" dur="3s"
|
||||
values="2;0" calcMode="linear"
|
||||
repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle cx="22" cy="22" r="8">
|
||||
<animate attributeName="r"
|
||||
begin="0s" dur="1.5s"
|
||||
values="6;1;2;3;4;5;6"
|
||||
calcMode="linear"
|
||||
repeatCount="indefinite" />
|
||||
</circle>
|
||||
</g>
|
||||
</svg>`;
|
||||
|
||||
function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
return function () {
|
||||
var context = this,
|
||||
args = arguments;
|
||||
var later = function () {
|
||||
timeout = null;
|
||||
if (!immediate)
|
||||
func.apply(context, args);
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow)
|
||||
func.apply(context, args);
|
||||
};
|
||||
};
|
||||
|
||||
function setCorrectShortcut() {
|
||||
if (navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
|
||||
document
|
||||
.querySelectorAll(".search-keys")
|
||||
.forEach(x => x.innerHTML = "⌘ + K");
|
||||
}
|
||||
}
|
||||
|
||||
function createIndex(posts) {
|
||||
const encoder = (str) => str
|
||||
.toLowerCase()
|
||||
.split(/([^a-z]|[^\x00-\x7F])/)
|
||||
const contentIndex = new FlexSearch.Document({
|
||||
cache: true,
|
||||
charset: "latin:extra",
|
||||
optimize: true,
|
||||
index: [
|
||||
{
|
||||
field: "content",
|
||||
tokenize: "reverse",
|
||||
encode: encoder
|
||||
}, {
|
||||
field: "title",
|
||||
tokenize: "forward",
|
||||
encode: encoder
|
||||
}, {
|
||||
field: "tags",
|
||||
tokenize: "forward",
|
||||
encode: encoder
|
||||
}
|
||||
]
|
||||
})
|
||||
posts.forEach((p, idx) => {
|
||||
contentIndex.add({
|
||||
id: idx, title: p.title, content: p.content, tags: p.tags //Change to removeHTML
|
||||
})
|
||||
});
|
||||
return contentIndex;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
//init offline search index
|
||||
|
||||
const searchIndexDate = '{{meta.buildDate|isoDate}}';
|
||||
let shouldFetch = true;
|
||||
if(localStorage.getItem("searchIndex")) {
|
||||
let {date, docs}= JSON.parse(localStorage.getItem('searchIndex'));
|
||||
if(date === searchIndexDate){
|
||||
shouldFetch = false;
|
||||
let index = createIndex(docs);
|
||||
window.docs = docs
|
||||
window.index = index;
|
||||
}
|
||||
}
|
||||
if(shouldFetch){
|
||||
let docs = await(await fetch('/searchIndex.json?v={{meta.buildDate|isoDate}}')).json();
|
||||
let index = createIndex(docs);
|
||||
localStorage.setItem("searchIndex", JSON.stringify({date: '{{meta.buildDate|isoDate}}', docs}));
|
||||
window.docs = docs
|
||||
window.index = index;
|
||||
}
|
||||
|
||||
//open searchmodal when ctrl + k is pressed, cmd + k on mac
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
toggleSearch();
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
document
|
||||
.getElementById('globalsearch')
|
||||
.classList
|
||||
.remove('active');
|
||||
}
|
||||
|
||||
//navigate search results with arrow keys
|
||||
if (document.getElementById('globalsearch').classList.contains('active')) {
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
let active = document.querySelector('.searchresult.active');
|
||||
if (active) {
|
||||
active
|
||||
.classList
|
||||
.remove('active');
|
||||
if (active.nextElementSibling) {
|
||||
active
|
||||
.nextElementSibling
|
||||
.classList
|
||||
.add('active');
|
||||
} else {
|
||||
document
|
||||
.querySelector('.searchresult')
|
||||
.classList
|
||||
.add('active');
|
||||
}
|
||||
} else {
|
||||
document
|
||||
.querySelector('.searchresult')
|
||||
.classList
|
||||
.add('active');
|
||||
}
|
||||
|
||||
let currentActive = document.querySelector('.searchresult.active');
|
||||
if (currentActive) {
|
||||
currentActive.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'});
|
||||
}
|
||||
}
|
||||
if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
let active = document.querySelector('.searchresult.active');
|
||||
if (active) {
|
||||
active
|
||||
.classList
|
||||
.remove('active');
|
||||
if (active.previousElementSibling) {
|
||||
active
|
||||
.previousElementSibling
|
||||
.classList
|
||||
.add('active');
|
||||
} else {
|
||||
document
|
||||
.querySelectorAll('.searchresult')
|
||||
.forEach((el) => {
|
||||
if (!el.nextElementSibling) {
|
||||
el
|
||||
.classList
|
||||
.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
document
|
||||
.querySelectorAll('.searchresult')
|
||||
.forEach((el) => {
|
||||
if (el.nextElementSibling) {
|
||||
el
|
||||
.classList
|
||||
.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let currentActive = document.querySelector('.searchresult.active');
|
||||
if (currentActive) {
|
||||
currentActive.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'});
|
||||
}
|
||||
|
||||
}
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
let active = document.querySelector('.searchresult.active');
|
||||
if (active) {
|
||||
window.location.href = active
|
||||
.querySelector("a")
|
||||
.href;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const debouncedSearch = debounce(search, 200, false);
|
||||
field = document.querySelector('#term');
|
||||
field.addEventListener('keydown', (e) => {
|
||||
if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') {
|
||||
debouncedSearch();
|
||||
}
|
||||
});
|
||||
resultsDiv = document.querySelector('#search-results');
|
||||
|
||||
const params = new URL(location.href).searchParams;
|
||||
if (params.get('q')) {
|
||||
field.setAttribute('value', params.get('q'));
|
||||
toggleSearch();
|
||||
search();
|
||||
}
|
||||
}
|
||||
window.lastSearch = '';
|
||||
async function search() {
|
||||
let search = field
|
||||
.value
|
||||
.trim();
|
||||
if (!search)
|
||||
return;
|
||||
if (search == lastSearch)
|
||||
return;
|
||||
console.log(`search for ${search}`);
|
||||
window.lastSearch = search;
|
||||
|
||||
resultsDiv.innerHTML = loadingSvg;
|
||||
//let searchRequest = await fetch(`/api/search?term=${encodeURIComponent(search)}`);
|
||||
//let results = await searchRequest.json();
|
||||
let results = offlineSearch(search);
|
||||
let resultsHTML = '';
|
||||
if (!results.length) {
|
||||
let resultParagraph = document.createElement("p");
|
||||
resultParagraph.innerText = `No results for "${search}"`;
|
||||
resultsDiv.innerHTML = '';
|
||||
resultsDiv.appendChild(resultParagraph);
|
||||
return;
|
||||
}
|
||||
resultsHTML += '<div style="max-width:100%;">';
|
||||
// we need to add title, url from ref
|
||||
results.forEach(r => {
|
||||
if(r.tags && r.tags.length > 0){
|
||||
resultsHTML += `<div class="searchresult">
|
||||
<a class="search-link" href="${r.url}">${r.title}</a>
|
||||
<div onclick="window.location='${r.url}'">
|
||||
<div class="header-meta">
|
||||
<div class="header-tags">
|
||||
${r.tags.map(tag=>'<a class="tag" href="JavaScript:Void(0);">#'+tag+'</a>').join("")}
|
||||
</div>
|
||||
</div>
|
||||
${r.content}
|
||||
</div>
|
||||
</div>`;
|
||||
} else {
|
||||
resultsHTML += `<div class="searchresult">
|
||||
<a class="search-link" href="${r.url}">${r.title}</a>
|
||||
<div onclick="window.location='${r.url}'">
|
||||
${r.content}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
resultsHTML += '</div>';
|
||||
resultsDiv.innerHTML = resultsHTML;
|
||||
}
|
||||
|
||||
function truncate(str, size) {
|
||||
//first, remove HTML
|
||||
str = str.replaceAll(/<[^>]*>/g, '');
|
||||
if (str.length < size)
|
||||
return str;
|
||||
return str.substring(0, size - 3) + '...';
|
||||
}
|
||||
|
||||
function offlineSearch(searchQuery) {
|
||||
let data = window.docs;
|
||||
|
||||
let isTagSearch = searchQuery[0] === "#" && searchQuery.length > 1;
|
||||
|
||||
let searchResults = isTagSearch
|
||||
? index.search(searchQuery.substring(1), [
|
||||
{
|
||||
field: "tags"
|
||||
}
|
||||
])
|
||||
: index.search(searchQuery, [
|
||||
{
|
||||
field: "title",
|
||||
limit: 5
|
||||
}, {
|
||||
field: "content",
|
||||
weight: 10
|
||||
}
|
||||
]);
|
||||
|
||||
const getByField = (field) => {
|
||||
const results = searchResults.filter((x) => x.field === field)
|
||||
if (results.length === 0) {
|
||||
return []
|
||||
} else {
|
||||
return [...results[0].result]
|
||||
}
|
||||
}
|
||||
const allIds = new Set([
|
||||
...getByField("title"),
|
||||
...getByField("content"),
|
||||
...getByField("tags")
|
||||
])
|
||||
const dataIds = [...allIds];
|
||||
const finalResults = dataIds.map((id) => {
|
||||
let result = data[id];
|
||||
result.content = truncate(result.content, 400);
|
||||
result.tags = result
|
||||
.tags
|
||||
.filter((x) => x != "gardenEntry" && x != "note"); //Note is automatically added by 11ty. GardenEntry is used internally to mark the home page
|
||||
|
||||
return result;
|
||||
})
|
||||
return finalResults;
|
||||
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,69 @@
|
|||
<aside>
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-container">
|
||||
{% for imp in dynamics.sidebar.top %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{%if settings.dgShowLocalGraph === true%}
|
||||
{%include "components/graphScript.njk"%}
|
||||
{%endif%}
|
||||
|
||||
{%if settings.dgShowToc === true%}
|
||||
{%set tocHtml= (content and (content|toc)) %}
|
||||
{%if tocHtml %}
|
||||
<div class="toc">
|
||||
<div class="toc-title-container">
|
||||
<div class="toc-title">
|
||||
On this page
|
||||
</div>
|
||||
</div>
|
||||
<div class="toc-container">
|
||||
{{ tocHtml | safe }}
|
||||
</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
|
||||
{%endif%}
|
||||
|
||||
{%if settings.dgShowBacklinks === true %}
|
||||
{%if settings.dgShowBacklinks === true %}
|
||||
<div class="backlinks">
|
||||
<div class="backlink-title" style="margin: 4px 0 !important;">Pages mentioning this page</div>
|
||||
<div class="backlink-list">
|
||||
{%- if page.url == "/" -%}
|
||||
{%- if graph.nodes[graph.homeAlias].backLinks.length === 0 -%}
|
||||
<div class="backlink-card">
|
||||
<span class="no-backlinks-message">No other pages mentions this page</span>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- for backlink in graph.nodes[graph.homeAlias].backLinks -%}
|
||||
{%- if graph.nodes[backlink].url != graph.homeAlias -%}
|
||||
<div class="backlink-card">
|
||||
{%- if not meta.noteIconsSettings.backlinks -%}<i icon-name="link"></i> {%- endif -%}<a href="{{graph.nodes[backlink].url}}" data-note-icon="{{graph.nodes[backlink].noteIcon}}" class="backlink">{{graph.nodes[backlink].title}}</a>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- else -%}
|
||||
{%- if graph.nodes[page.url].backLinks.length === 0 -%}
|
||||
<div class="backlink-card">
|
||||
<span class="no-backlinks-message">No other pages mentions this page</span>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- for backlink in graph.nodes[page.url].backLinks -%}
|
||||
{%- if graph.nodes[backlink].url != page.url -%}
|
||||
<div class="backlink-card">
|
||||
{%- if not meta.noteIconsSettings.backlinks -%}<i icon-name="link"></i> {%- endif -%}<a href="{{graph.nodes[backlink].url}}" data-note-icon="{{graph.nodes[backlink].noteIcon}}" class="backlink">{{graph.nodes[backlink].title}}</a>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
{%endif%}
|
||||
{% for imp in dynamics.sidebar.bottom %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
|
@ -0,0 +1,7 @@
|
|||
<script src=" https://fastly.jsdelivr.net/npm/luxon@3.2.1/build/global/luxon.min.js "></script>
|
||||
<script defer>
|
||||
TIMESTAMP_FORMAT = "{{meta.timestampSettings.timestampFormat}}";
|
||||
document.querySelectorAll('.human-date').forEach(function (el) {
|
||||
el.innerHTML = luxon.DateTime.fromISO(el.getAttribute('data-date') || el.innerText).toFormat(TIMESTAMP_FORMAT);
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,79 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{% if title %}{{ title }}{% else %}{{ page.fileSlug }}{% endif %}</title>
|
||||
{%include "components/pageheader.njk"%}
|
||||
{% for imp in dynamics.common.head %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.index.head %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
</head>
|
||||
<body class="theme-{{meta.baseTheme}} markdown-preview-view markdown-rendered markdown-preview-section {{meta.bodyClasses}}">
|
||||
{%include "components/notegrowthhistory.njk"%}
|
||||
{% if settings.dgShowFileTree !== true %}
|
||||
{%include "components/navbar.njk"%}
|
||||
{%else%}
|
||||
{%include "components/filetree.njk"%}
|
||||
{% endif %}
|
||||
{% if settings.dgEnableSearch === true %}
|
||||
{%include "components/searchContainer.njk"%}
|
||||
{% endif %}
|
||||
<main class="content cm-s-obsidian {{contentClasses}}">
|
||||
<header>
|
||||
{% if settings.dgShowInlineTitle === true %}
|
||||
<h1>{{ noteTitle }}</h1>
|
||||
{% endif %}
|
||||
|
||||
<div class="header-meta">
|
||||
{% if settings.dgShowTags === true and tags %}
|
||||
<div class="header-tags">
|
||||
{% for tag in tags %}
|
||||
{% if tag != 'gardenEntry' and tag !='note' %}
|
||||
<a class="tag" onclick="toggleTagSearch(this)">
|
||||
#{{tag}}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% for imp in dynamics.common.header %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.index.header %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
</header>
|
||||
{% for imp in dynamics.common.beforeContent %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.index.beforeContent %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{{ content | hideDataview | link | taggify | safe}}
|
||||
{% for imp in dynamics.common.afterContent %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.index.afterContent %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
</main>
|
||||
|
||||
{% if settings.dgShowBacklinks === true or settings.dgShowLocalGraph === true or settings.dgShowToc === true%}
|
||||
{%include "components/sidebar.njk" %}
|
||||
{%endif%}
|
||||
|
||||
{% if settings.dgLinkPreview === true %}
|
||||
{%include "components/linkPreview.njk"%}
|
||||
{% endif %}
|
||||
{% for imp in dynamics.common.footer %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.index.footer %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{%include "components/lucideIcons.njk"%}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
permalink: "notes/{{ page.fileSlug | slugify }}/"
|
||||
---
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{% if title %}{{ title }}{% else %}{{ page.fileSlug }}{% endif %}</title>
|
||||
{%include "components/pageheader.njk"%}
|
||||
{% for imp in dynamics.common.head %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.notes.head %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
</head>
|
||||
<body class="theme-{{meta.baseTheme}} markdown-preview-view markdown-rendered markdown-preview-section {{meta.bodyClasses}}">
|
||||
{%include "components/notegrowthhistory.njk"%}
|
||||
|
||||
{% if settings.dgShowFileTree !== true %}
|
||||
{%include "components/navbar.njk"%}
|
||||
{%else%}
|
||||
{%include "components/filetree.njk"%}
|
||||
{% endif %}
|
||||
|
||||
{% if settings.dgEnableSearch === true %}
|
||||
{%include "components/searchContainer.njk"%}
|
||||
{% endif %}
|
||||
|
||||
<main class="content cm-s-obsidian {{contentClasses}}">
|
||||
<header>
|
||||
{% if settings.dgShowInlineTitle === true %}
|
||||
<h1 data-note-icon="{% if noteIcon %}{{noteIcon}}{% else %}{{meta.noteIconsSettings.default}}{% endif %}">{% if title %}{{ title }}{% else %}{{ page.fileSlug }}{% endif %}</h1>
|
||||
{% endif %}
|
||||
<div class="header-meta">
|
||||
{% if settings.dgShowTags === true and tags %}
|
||||
<div class="header-tags">
|
||||
{% for tag in tags %}
|
||||
{% if tag != 'gardenEntry' and tag !='note' %}
|
||||
<a class="tag" onclick="toggleTagSearch(this)">
|
||||
#{{tag}}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{%- if meta.timestampSettings.showCreated or meta.timestampSettings.showUpdated -%}
|
||||
<div class="timestamps">
|
||||
{%- if meta.timestampSettings.showCreated and created -%}
|
||||
<div><i icon-name="calendar-plus"></i> <span class="human-date" data-date="{{created}}"></span></div>
|
||||
{%- endif -%}
|
||||
{%- if meta.timestampSettings.showUpdated and updated -%}
|
||||
<div><i icon-name="calendar-clock"></i> <span class="human-date" data-date="{{updated}}"></span></div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
{% for imp in dynamics.common.header %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.notes.header %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
</header>
|
||||
{% for imp in dynamics.common.beforeContent %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.notes.beforeContent %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{{ content | hideDataview | link | taggify | safe}}
|
||||
{% for imp in dynamics.common.afterContent %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.notes.afterContent %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
</main>
|
||||
|
||||
{% if settings.dgShowBacklinks === true or settings.dgShowLocalGraph === true or settings.dgShowToc === true%}
|
||||
{%include "components/sidebar.njk"%}
|
||||
{% endif %}
|
||||
|
||||
{% if settings.dgLinkPreview === true %}
|
||||
{%include "components/linkPreview.njk"%}
|
||||
{% endif %}
|
||||
{% include "components/references.njk" %}
|
||||
{% include "components/timestamps.njk" %}
|
||||
{% for imp in dynamics.common.footer %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{% for imp in dynamics.notes.footer %}
|
||||
{% include imp %}
|
||||
{% endfor %}
|
||||
{%include "components/lucideIcons.njk"%}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,24 @@
|
|||
<svg id="emoji" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="color">
|
||||
<rect x="37.0753" y="36.0532" width="26.8836" height="18.1139" fill="#FFFFFF"/>
|
||||
<polygon fill="#EA5A47" stroke="#EA5A47" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" points="50.2353,23.2 38.1,36 63.5,36"/>
|
||||
<path fill="#EA5A47" d="M47.3,26l-6.4,6.0595v-8.5733c0-0.9313,0.7549-1.4162,1.6862-1.4162h3.0276 c0.9313,0,1.6862,0.4849,1.6862,1.4162C47.3,23.4862,47.3,26,47.3,26z"/>
|
||||
<rect x="43" y="42" width="6" height="12" fill="#A57939"/>
|
||||
<rect x="54" y="42" width="6" height="5" fill="#92D3F5"/>
|
||||
<path fill="#B1CC33" d="M41.0059,28.3393c0-3.3123-3.4447-6.0025-7.6937-6.0025c-0.3549,0.0029-0.7095,0.025-1.0622,0.0662 c-1.7995-3.4553-5.7065-5.8554-10.2499-5.8554c-6.2478,0-11.3122,4.5286-11.3122,10.1144c0.0012,0.3692,0.0247,0.738,0.0704,1.1043 C7.9656,28.6665,6,30.8102,6,33.3136c0,3.3123,3.4446,6.0025,7.6936,6.0025c1.6151,0.0139,3.2056-0.4022,4.6137-1.207 c1.8177,1.4766,4.7264,2.4385,8.0154,2.4385c5.5105,0,9.9776-2.6875,9.9776-6.0025c-0.0009-0.2191-0.0214-0.4377-0.0612-0.6531 C39.0357,32.9904,41.0059,30.8453,41.0059,28.3393z"/>
|
||||
</g>
|
||||
<g id="hair"/>
|
||||
<g id="skin"/>
|
||||
<g id="skin-shadow"/>
|
||||
<g id="line">
|
||||
<polyline fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" points="46,27 50.2353,23 64,35.0976 64,54 38,54 38,41.5482"/>
|
||||
<rect x="43" y="42" width="6" height="12" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<rect x="54" y="42" width="6" height="5" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<polyline fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" points="43.5,23 46.5,23 46.5,26"/>
|
||||
<path fill="none" stroke="#000000" stroke-miterlimit="10" stroke-width="2" d="M31.245,39.3396"/>
|
||||
<polyline fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" points="22.5,54 22.5,46.8269 17.5,42"/>
|
||||
<line x1="23.0408" x2="25.885" y1="46.7265" y2="43.7657" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
|
||||
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M41.0059,28.3393 c0-3.3123-3.4447-6.0025-7.6937-6.0025c-0.3549,0.0029-0.7095,0.025-1.0622,0.0662c-1.7995-3.4553-5.7065-5.8554-10.2499-5.8554 c-6.2478,0-11.3122,4.5286-11.3122,10.1144c0.0012,0.3692,0.0247,0.738,0.0704,1.1043C7.9656,28.6665,6,30.8102,6,33.3136 c0,3.3123,3.4446,6.0025,7.6936,6.0025c1.6151,0.0139,3.2056-0.4022,4.6137-1.207c1.8177,1.4766,4.7264,2.4385,8.0154,2.4385 c5.5105,0,9.9776-2.6875,9.9776-6.0025c-0.0009-0.2191-0.0214-0.4377-0.0612-0.6531 C39.0357,32.9904,41.0059,30.8453,41.0059,28.3393z"/>
|
||||
<line x1="63.5" x2="42.5" y1="36" y2="36" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,30 @@
|
|||
---json
|
||||
{
|
||||
"permalink": "/feed.xml",
|
||||
"eleventyExcludeFromCollections": true
|
||||
}
|
||||
---
|
||||
{%if meta.siteBaseUrl %}<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="{{ meta.siteBaseUrl }}">
|
||||
<title>{{ meta.siteName}}</title>
|
||||
<link href="{{ meta.siteBaseUrl }}{{ permalink }}" rel="self" ////>
|
||||
<link href="{{ meta.siteBaseUrl }}" ////>
|
||||
<updated>{{ collections.notes | getNewestCollectionItemDate | dateToRfc3339 }}</updated>
|
||||
<id>{{ meta.siteBaseUrl }}</id>
|
||||
{%- for note in collections.note | reverse %}
|
||||
<entry>
|
||||
<title>
|
||||
{% if note.title %}{{ note.title }}
|
||||
{% else %}{{ note.fileSlug }}
|
||||
{% endif %}
|
||||
</title>
|
||||
<updated>{%if note.data.updated %}{{ note.data.updated | dateToZulu }}{%else%}{{ note.date | dateToRfc3339 }}{%endif%}</updated>
|
||||
<id>{{ meta.siteBaseUrl }}{{note.url | url }}</id>
|
||||
<content type="html">
|
||||
{{ note.templateContent | hideDataview | link | taggify | htmlToAbsoluteUrls(meta.siteBaseUrl) }}
|
||||
</content>
|
||||
<link href="{{ meta.siteBaseUrl }}{{note.url | url }}" ////>
|
||||
</entry>
|
||||
{%- endfor %}
|
||||
</feed>
|
||||
{% endif %}
|
|
@ -0,0 +1,47 @@
|
|||
require("dotenv").config();
|
||||
const axios = require("axios");
|
||||
const fs = require("fs");
|
||||
const crypto = require("crypto");
|
||||
const {globSync} = require("glob");
|
||||
|
||||
const themeCommentRegex = /\/\*[\s\S]*?\*\//g;
|
||||
|
||||
async function getTheme() {
|
||||
let themeUrl = process.env.THEME;
|
||||
if (themeUrl) {
|
||||
//https://forum.obsidian.md/t/1-0-theme-migration-guide/42537
|
||||
//Not all themes with no legacy mark have a theme.css file, so we need to check for it
|
||||
try {
|
||||
await axios.get(themeUrl);
|
||||
} catch {
|
||||
if (themeUrl.indexOf("theme.css") > -1) {
|
||||
themeUrl = themeUrl.replace("theme.css", "obsidian.css");
|
||||
} else if (themeUrl.indexOf("obsidian.css") > -1) {
|
||||
themeUrl = themeUrl.replace("obsidian.css", "theme.css");
|
||||
}
|
||||
}
|
||||
|
||||
const res = await axios.get(themeUrl);
|
||||
try {
|
||||
const existing = globSync("src/site/styles/_theme.*.css");
|
||||
existing.forEach((file) => {
|
||||
fs.rmSync(file);
|
||||
});
|
||||
} catch {}
|
||||
let skippedFirstComment = false;
|
||||
const data = res.data.replace(themeCommentRegex, (match) => {
|
||||
if (skippedFirstComment) {
|
||||
return "";
|
||||
} else {
|
||||
skippedFirstComment = true;
|
||||
return match;
|
||||
}
|
||||
});
|
||||
const hashSum = crypto.createHash("sha256");
|
||||
hashSum.update(data);
|
||||
const hex = hashSum.digest("hex");
|
||||
fs.writeFileSync(`src/site/styles/_theme.${hex.substring(0, 8)}.css`, data);
|
||||
}
|
||||
}
|
||||
|
||||
getTheme();
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
permalink: /graph.json
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
{{ graph | jsonify | safe }}
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
|
||||
<polyline points="14 2 14 8 20 8"></polyline>
|
||||
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||||
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||||
<line x1="10" y1="9" x2="8" y2="9"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 479 B |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns='http://www.w3.org/2000/svg' class='i-external' viewBox='0 0 32 32' width='14' height='14' fill='none' stroke='#888888' stroke-linecap='round' stroke-linejoin='round' stroke-width='9.38%'><path d='M14 9 L3 9 3 29 23 29 23 18 M18 4 L28 4 28 14 M28 4 L14 18'/></svg>
|
After Width: | Height: | Size: 315 B |
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="150" height="205" version="1.1" viewBox="0 0 39.688 54.24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(-69.7 -93.956)" fill="none" stroke="#008000">
|
||||
<path d="m69.7 146.87h39.688" stroke-width="2.6458"/>
|
||||
<g transform="translate(-.36252)">
|
||||
<path d="m89.544 146.87v-6.794" stroke-width="2.6458"/>
|
||||
<path d="m88.77 141.34 6.6272-8.1886" stroke-width="2.3347"/>
|
||||
<path d="m89.919 141.46-5.5766-5.8386" stroke-width="2.3102"/>
|
||||
</g>
|
||||
<circle cx="100.95" cy="126.47" r="6.9136" stroke-width="2.6458"/>
|
||||
<circle cx="79.351" cy="130.4" r="5.0854" stroke-width="2.6458"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 657 B |
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="150" height="205" version="1.1" viewBox="0 0 39.688 54.24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(0 -.8262)" fill="none" stroke="#20b2aa">
|
||||
<circle cx="33.971" cy="33.263" r="4.79" stroke-width="1.8521"/>
|
||||
<circle cx="5.716" cy="33.263" r="4.79" stroke-width="1.8521"/>
|
||||
<g stroke-width="2.6458">
|
||||
<path d="m6.8958 53.743h25.896"/>
|
||||
<path d="m19.844 53.743v-25.896"/>
|
||||
<circle cx="19.844" cy="18.683" r="7.0212"/>
|
||||
<path d="m6.8958 40.795 12.948 3.237 12.948-3.237"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 630 B |
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="150" height="205" version="1.1" viewBox="0 0 39.688 54.24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="#2e8b57">
|
||||
<g>
|
||||
<circle cx="34.846" cy="31.29" r="3.9152" stroke-width="1.8521"/>
|
||||
<circle cx="4.8413" cy="31.29" r="3.9152" stroke-width="1.8521"/>
|
||||
<path d="m9.2604 52.775h21.167" stroke-width="2.6458"/>
|
||||
</g>
|
||||
<path d="m19.844 53.834v-37.849" stroke-width="2.5838"/>
|
||||
<circle cx="19.844" cy="7.1851" r="5.739" stroke-width="2.6458"/>
|
||||
<path d="m6.6146 37.959 5.2917 5.2917h7.9375" stroke-width="2.6458"/>
|
||||
<path d="m33.073 37.959-5.2917 5.2917h-7.9375" stroke-width="2.6458"/>
|
||||
<circle cx="31.804" cy="17.056" r="3.9152" stroke-width="1.3229"/>
|
||||
<circle cx="7.884" cy="17.056" r="3.9152" stroke-width="1.3229"/>
|
||||
<path d="m9.2604 23.406 10.583 2.6458 10.583-2.6458" stroke-width="2.6458"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 949 B |
|
@ -0,0 +1,33 @@
|
|||
require("dotenv").config();
|
||||
const settings = require("../../helpers/constants");
|
||||
|
||||
const allSettings = settings.ALL_NOTE_SETTINGS;
|
||||
|
||||
module.exports = {
|
||||
eleventyComputed: {
|
||||
layout: (data) => {
|
||||
if (data.tags.indexOf("gardenEntry") != -1) {
|
||||
return "layouts/index.njk";
|
||||
}
|
||||
return "layouts/note.njk";
|
||||
},
|
||||
permalink: (data) => {
|
||||
if (data.tags.indexOf("gardenEntry") != -1) {
|
||||
return "/";
|
||||
}
|
||||
return data.permalink || undefined;
|
||||
},
|
||||
settings: (data) => {
|
||||
const noteSettings = {};
|
||||
allSettings.forEach((setting) => {
|
||||
let noteSetting = data[setting];
|
||||
let globalSetting = process.env[setting];
|
||||
|
||||
let settingValue =
|
||||
noteSetting || (globalSetting === "true" && noteSetting !== false);
|
||||
noteSettings[setting] = settingValue;
|
||||
});
|
||||
return noteSettings;
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"tags": "note",
|
||||
"templateEngineOverride": "njk,md"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
permalink: /searchIndex.json
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
[{% for post in collections.note %}
|
||||
{
|
||||
"title": {% if post.data.title %}{{post.data.title | jsonify | safe }}{% else %}{{post.fileSlug | jsonify | safe }}{% endif %},
|
||||
"date":"{{ post.date }}",
|
||||
"url":"{{ post.url }}",
|
||||
"content": {{ post.templateContent | striptags(true) | link | jsonify | safe }},
|
||||
"tags": [{{post.templateContent | link | searchableTags | safe }} {% if post.data.tags %}{% for tag in post.data.tags %}"{{tag|validJson}}"{% if not loop.last %},{% endif %}{% endfor %}{% endif %}]
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}]
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
permalink: /sitemap.xml
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{% for page in collections.all %}
|
||||
<url>
|
||||
<loc>{{ meta.siteBaseUrl }}{{ page.url | url }}</loc>
|
||||
<lastmod>{{ page.date.toISOString() }}</lastmod>
|
||||
</url>
|
||||
{% endfor %}
|
||||
</urlset>
|
|
@ -0,0 +1,13 @@
|
|||
body {
|
||||
/***
|
||||
ADD YOUR CUSTOM STYLIING HERE. (INSIDE THE body {...} section.)
|
||||
IT WILL TAKE PRECEDENCE OVER THE STYLING IN THE STYLE.CSS FILE.
|
||||
***/
|
||||
// background-color: white;
|
||||
// .content {
|
||||
// font-size: 14px;
|
||||
// }
|
||||
// h1 {
|
||||
// color: black;
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,753 @@
|
|||
/***
|
||||
* DO NOT ADD/MODIFY STYLING HERE. IT WILL BE OVERWRITTEN IF YOU UPDATE THE TEMPLATE VERSION.
|
||||
* MODIFY THE custom-style.scss FILE INSTEAD.
|
||||
***/
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
--note-icon-1: url(/img/tree-1.svg);
|
||||
--note-icon-2: url(/img/tree-2.svg);
|
||||
--note-icon-3: url(/img/tree-3.svg);
|
||||
--note-icon-fallback: url(/img/default-note-icon.svg);
|
||||
--graph-main: var(--text-accent);
|
||||
--graph-muted: var(--text-muted);
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
font-size: 18px;
|
||||
line-height: 1.5;
|
||||
margin-top: 90px;
|
||||
position: relative;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
margin-top: 75px;
|
||||
}
|
||||
}
|
||||
|
||||
.external-link {
|
||||
background-position: center right;
|
||||
background-repeat: no-repeat;
|
||||
background-image: linear-gradient(transparent, transparent), url("/img/outgoing.svg");
|
||||
background-size: 13px;
|
||||
padding-right: 16px;
|
||||
background-position-y: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.markdown-preview-view pre.mermaid {
|
||||
background: white;
|
||||
border-radius: 25px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
div.transclusion {
|
||||
position: relative;
|
||||
padding: 8px;
|
||||
.markdown-embed-link {
|
||||
z-index: 99;
|
||||
display: block;
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
ul.task-list {
|
||||
list-style: none;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 75px;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
min-width: 25px;
|
||||
display: flex;
|
||||
z-index: 3;
|
||||
max-width: 350px;
|
||||
|
||||
.graph {
|
||||
width: 320px;
|
||||
min-height: 320px;
|
||||
|
||||
#link-graph {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
#graph-fs-btn {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.graph-fs {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
height: calc(100vmin - 100px);
|
||||
width: calc(100vmin - 100px);
|
||||
min-height: 350px;
|
||||
min-width: 350px;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 9999;
|
||||
display: block;
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px solid var(--text-accent);
|
||||
border-radius: 5px;
|
||||
padding-top: 5px;
|
||||
|
||||
#link-graph {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#graph-controls {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.graph-title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expand-line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
padding-right: 20px;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
height: 87%;
|
||||
}
|
||||
|
||||
.toc {
|
||||
padding-right: 5px;
|
||||
background-color: var(--background-primary);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
padding-left: 10px;
|
||||
border-left: 2px solid var(--background-secondary);
|
||||
}
|
||||
|
||||
& > ol {
|
||||
padding-left: 0;
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
.toc-container {
|
||||
font-size: 1rem;
|
||||
max-height: 220px;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 10px;
|
||||
border-left: 1px solid var(--text-accent);
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-inline-start: 15px !important;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ul:not(:first-child) {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding-top: 4px;
|
||||
&::before {
|
||||
content: "# " !important;
|
||||
color: var(--text-accent);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toc-title-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
.toc-title {
|
||||
font-size: 1.2rem !important;
|
||||
color: var(--h6-color);
|
||||
width: fit-content;
|
||||
padding: 3px 7px 3px 0;
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.backlinks {
|
||||
flex: 1;
|
||||
margin-top: 10px;
|
||||
background-color: var(--background-primary);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.backlink-title {
|
||||
margin: 4px 0;
|
||||
font-size: 18px !important;
|
||||
color: var(--h6-color);
|
||||
}
|
||||
}
|
||||
|
||||
.backlink-list {
|
||||
border-left: 1px solid var(--text-accent);
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.backlink-card {
|
||||
padding-bottom: 8px;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
font-size: 1rem;
|
||||
margin-left: 10px;
|
||||
color: var(--text-accent);
|
||||
i {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.backlink {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-backlinks-message {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.graph {
|
||||
.graph-title-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.graph-title {
|
||||
width: fit-content;
|
||||
background-color: var(--background-secondary);
|
||||
margin: 10px 0 0 0;
|
||||
padding: 3px 7px;
|
||||
font-size: 1.2rem !important;
|
||||
border-radius: 10px 10px 0 0;
|
||||
color: var(--h6-color);
|
||||
}
|
||||
}
|
||||
|
||||
#link-graph {
|
||||
background-color: var(--background-secondary);
|
||||
margin-bottom: 20px;
|
||||
border-radius: 10px 0 10px 10px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
@media (max-width: 1400px) {
|
||||
#link-graph {
|
||||
border-radius: 0 10px 10px 10px;
|
||||
}
|
||||
.sidebar {
|
||||
position: relative;
|
||||
transform: none;
|
||||
border-radius: 4px;
|
||||
margin-top: 50px;
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
border-radius: 0;
|
||||
border-top: 2px solid var(--background-secondary);
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.graph {
|
||||
flex: 1;
|
||||
.graph-title-container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.toc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
.sidebar-container {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.filetree-sidebar {
|
||||
margin: 0;
|
||||
z-index: 10;
|
||||
padding: 10px;
|
||||
top: 0px;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
background-color: var(--background-secondary);
|
||||
color: var(--text-muted);
|
||||
overflow-y: auto;
|
||||
width: 250px;
|
||||
h1 {
|
||||
font-size: 32px !important;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-button {
|
||||
width: 100%;
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-navbar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: -60px;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-button {
|
||||
margin: 10px 65px 10px 65px;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: var(--background-secondary);
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
padding-left: var(--file-margins);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@media (max-width: 1400px) {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
h1 {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-button {
|
||||
@media (max-width: 800px) {
|
||||
min-width: 36px;
|
||||
margin: 10px 25px 10px 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-text {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@media (max-width: 800px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notelink {
|
||||
padding: 5px 0 5px 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
a {
|
||||
&:hover {
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
}
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.foldername-wrapper {
|
||||
cursor: pointer;
|
||||
margin: 4px 0 4px 2px;
|
||||
}
|
||||
|
||||
.inner-folder {
|
||||
padding: 3px 0 3px 10px;
|
||||
}
|
||||
|
||||
.filename {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.notelink.active-note {
|
||||
a {
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
color: var(--text-accent);
|
||||
background-color: var(--background-primary);
|
||||
transform: translateX(10px);
|
||||
}
|
||||
|
||||
.fullpage-overlay {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 15;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-container.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
transform: translateY(100px);
|
||||
background-color: var(--background-primary);
|
||||
max-width: 80%;
|
||||
width: 900px;
|
||||
border-radius: 15px;
|
||||
padding: 10px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 2rem;
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.search-box input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#search-results {
|
||||
margin-top: 20px;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
#search-results .searchresult {
|
||||
margin-bottom: 15px;
|
||||
list-style: none;
|
||||
background-color: var(--background-secondary);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border: 2px solid var(--text-accent);
|
||||
}
|
||||
}
|
||||
|
||||
.search-box-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.navigation-hint {
|
||||
font-size: 1rem;
|
||||
color: var(--text-secondary);
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.search-link {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.search-button {
|
||||
background-color: var(--background-primary);
|
||||
border-radius: 20px;
|
||||
height: 2em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 150px;
|
||||
margin: 10px 40px;
|
||||
border: 1px solid var(--text-normal);
|
||||
cursor: pointer;
|
||||
|
||||
> i {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 1px solid var(--text-accent);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.search-keys {
|
||||
@media (max-width: 800px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.callout-title-inner,
|
||||
.callout-title-inner p,
|
||||
.callout-icon,
|
||||
.callout-fold,
|
||||
.callout-content {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.callout-fold {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.callout-title {
|
||||
margin-top: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.callout {
|
||||
font-family: var(--font-default);
|
||||
word-wrap: break-word;
|
||||
display: block;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.callout.is-collapsed {
|
||||
.callout-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.callout-fold .lucide {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.callout-fold .lucide {
|
||||
transition: transform 100ms ease-in-out;
|
||||
}
|
||||
|
||||
.referred {
|
||||
border: 1px dashed;
|
||||
border-color: var(--text-accent);
|
||||
padding: 10px;
|
||||
margin-left: -10px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
|
||||
// Graph Controls
|
||||
.graph-title-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#full-graph {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
height: calc(100vmin - 100px);
|
||||
width: calc(100vmin - 100px);
|
||||
min-height: 350px;
|
||||
min-width: 350px;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 9999;
|
||||
display: none;
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px solid var(--text-accent);
|
||||
border-radius: 5px;
|
||||
|
||||
#full-graph-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#full-graph-close {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
z-index: 9;
|
||||
}
|
||||
}
|
||||
|
||||
#graph-full-btn {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#full-graph.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#graph-controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 5px;
|
||||
position: absolute;
|
||||
top: 115%;
|
||||
cursor: pointer;
|
||||
right: 0px;
|
||||
left: 10px;
|
||||
color: var(--text-accent);
|
||||
z-index: 9;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.depth-control {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
gap: 7px;
|
||||
|
||||
.slider {
|
||||
datalist {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
font-size: 0.6rem;
|
||||
// padding: 2px 0px;
|
||||
// width: 200px;
|
||||
}
|
||||
|
||||
option {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#depth-display {
|
||||
background-color: var(--text-accent);
|
||||
color: white;
|
||||
width: 1.3rem;
|
||||
height: 1.3rem;
|
||||
font-size: 0.8rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 0.3rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
body.title-note-icon .cm-s-obsidian > header > h1[data-note-icon]::before,
|
||||
body.filetree-note-icon .filename[data-note-icon]::before,
|
||||
body.links-note-icon .internal-link[data-note-icon]::before,
|
||||
body.backlinks-note-icon .backlink[data-note-icon]::before {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
width: 0.9em;
|
||||
height: 1em;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: bottom;
|
||||
background-image: var(--note-icon-fallback);
|
||||
}
|
||||
|
||||
body.title-note-icon .cm-s-obsidian > header > h1[data-note-icon="1"]::before,
|
||||
body.filetree-note-icon .filename[data-note-icon="1"]::before,
|
||||
body.links-note-icon .internal-link[data-note-icon="1"]::before,
|
||||
body.backlinks-note-icon .backlink[data-note-icon="1"]::before {
|
||||
background-image: var(--note-icon-1);
|
||||
}
|
||||
|
||||
body.title-note-icon .cm-s-obsidian > header > h1[data-note-icon="2"]::before,
|
||||
body.filetree-note-icon .filename[data-note-icon="2"]::before,
|
||||
body.links-note-icon .internal-link[data-note-icon="2"]::before,
|
||||
body.backlinks-note-icon .backlink[data-note-icon="2"]::before {
|
||||
background-image: var(--note-icon-2);
|
||||
}
|
||||
|
||||
body.title-note-icon .cm-s-obsidian > header > h1[data-note-icon="3"]::before,
|
||||
body.filetree-note-icon .filename[data-note-icon="3"]::before,
|
||||
body.links-note-icon .internal-link[data-note-icon="3"]::before,
|
||||
body.backlinks-note-icon .backlink[data-note-icon="3"]::before {
|
||||
background-image: var(--note-icon-3);
|
||||
}
|
||||
|
||||
.timestamps {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 0.8em;
|
||||
color: var(--text-muted);
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 3px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.align-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.cm-s-obsidian {
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,219 @@
|
|||
/***
|
||||
* DO NOT ADD/MODIFY STYLING HERE. IT WILL BE OVERWRITTEN IF YOU UPDATE THE TEMPLATE VERSION.
|
||||
* MODIFY THE custom-style.scss FILE INSTEAD.
|
||||
***/
|
||||
|
||||
:root {
|
||||
--background-primary: rgb(32, 31, 31);
|
||||
--background-secondary: rgb(57, 56, 56);
|
||||
--text-normal: #dcddde;
|
||||
--text-accent: rgb(97, 186, 245);
|
||||
--file-margins: 32px;
|
||||
|
||||
--callout-border-width: 0px;
|
||||
--callout-border-opacity: 0.25;
|
||||
--callout-padding: 12px 12px 12px 24px;
|
||||
--callout-radius: 4px;
|
||||
--callout-blend-mode: lighten;
|
||||
--callout-title-padding: 0;
|
||||
--callout-title-size: inherit;
|
||||
--callout-content-padding: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #ffef60;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #f06449;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #d4fcc3;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: #72dcff;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.centered {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.theme-dark {
|
||||
background: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
font-family: var(--font-default);
|
||||
}
|
||||
|
||||
.theme-light {
|
||||
background: white;
|
||||
color: black;
|
||||
font-family: var(--font-default);
|
||||
}
|
||||
|
||||
a.is-unresolved {
|
||||
color: rgb(97 186 245 / 65%);
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.font-bg {
|
||||
font-size: 92px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
background: #ffffff17;
|
||||
border-left: 10px solid #c1dbe3;
|
||||
margin: 1.5em 10px;
|
||||
padding: 0.5em 10px;
|
||||
quotes: "\201C""\201D""\2018""\2019";
|
||||
}
|
||||
|
||||
blockquote:before {
|
||||
color: #ccc;
|
||||
content: open-quote;
|
||||
font-size: 4em;
|
||||
line-height: 0.1em;
|
||||
margin-right: 0.1em;
|
||||
vertical-align: -0.4em;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p > code {
|
||||
//Inline code
|
||||
color: #c7254e;
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
|
||||
.callout {
|
||||
--callout-color: 68, 138, 255;
|
||||
--callout-icon: lucide-pencil;
|
||||
}
|
||||
.callout[data-callout="abstract"],
|
||||
.callout[data-callout="summary"],
|
||||
.callout[data-callout="tldr"] {
|
||||
--callout-color: 0, 176, 255;
|
||||
--callout-icon: lucide-clipboard-list;
|
||||
}
|
||||
.callout[data-callout="info"],
|
||||
.callout[data-callout="todo"] {
|
||||
--callout-color: 0, 184, 212;
|
||||
}
|
||||
.callout[data-callout="info"] {
|
||||
--callout-icon: lucide-info;
|
||||
}
|
||||
.callout[data-callout="todo"] {
|
||||
--callout-icon: lucide-check-circle-2;
|
||||
}
|
||||
.callout[data-callout="tip"],
|
||||
.callout[data-callout="hint"],
|
||||
.callout[data-callout="important"] {
|
||||
--callout-color: 0, 191, 165;
|
||||
--callout-icon: lucide-flame;
|
||||
}
|
||||
.callout[data-callout="success"],
|
||||
.callout[data-callout="check"],
|
||||
.callout[data-callout="done"] {
|
||||
--callout-color: 0, 200, 83;
|
||||
--callout-icon: lucide-check;
|
||||
}
|
||||
.callout[data-callout="question"],
|
||||
.callout[data-callout="help"],
|
||||
.callout[data-callout="faq"] {
|
||||
--callout-color: 100, 221, 23;
|
||||
--callout-icon: help-circle;
|
||||
}
|
||||
.callout[data-callout="warning"],
|
||||
.callout[data-callout="caution"],
|
||||
.callout[data-callout="attention"] {
|
||||
--callout-color: 255, 145, 0;
|
||||
--callout-icon: lucide-alert-triangle;
|
||||
}
|
||||
.callout[data-callout="failure"],
|
||||
.callout[data-callout="fail"],
|
||||
.callout[data-callout="missing"] {
|
||||
--callout-color: 255, 82, 82;
|
||||
--callout-icon: lucide-x;
|
||||
}
|
||||
.callout[data-callout="danger"],
|
||||
.callout[data-callout="error"] {
|
||||
--callout-color: 255, 23, 68;
|
||||
--callout-icon: lucide-zap;
|
||||
}
|
||||
.callout[data-callout="bug"] {
|
||||
--callout-color: 245, 0, 87;
|
||||
--callout-icon: lucide-bug;
|
||||
}
|
||||
.callout[data-callout="example"] {
|
||||
--callout-color: 124, 77, 255;
|
||||
--callout-icon: lucide-list;
|
||||
}
|
||||
.callout[data-callout="quote"],
|
||||
.callout[data-callout="cite"] {
|
||||
--callout-color: 158, 158, 158;
|
||||
--callout-icon: lucide-quote;
|
||||
}
|
||||
.callout {
|
||||
overflow: hidden;
|
||||
border-style: solid;
|
||||
border-color: rgba(var(--callout-color), var(--callout-border-opacity));
|
||||
border-width: var(--callout-border-width);
|
||||
border-radius: var(--callout-radius);
|
||||
margin: 1em 0;
|
||||
mix-blend-mode: var(--callout-blend-mode);
|
||||
background-color: rgba(var(--callout-color), 0.1);
|
||||
padding: var(--callout-padding);
|
||||
}
|
||||
.callout.is-collapsible .callout-title {
|
||||
cursor: default;
|
||||
}
|
||||
.callout-title {
|
||||
padding: var(--callout-title-padding);
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
font-size: var(--callout-title-size);
|
||||
color: rgb(var(--callout-color));
|
||||
line-height: 1.3;
|
||||
}
|
||||
.callout-content {
|
||||
overflow-x: auto;
|
||||
padding: var(--callout-content-padding);
|
||||
}
|
||||
.callout-icon {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-self: center;
|
||||
}
|
||||
.callout-icon .svg-icon {
|
||||
color: rgb(var(--callout-color));
|
||||
}
|
||||
.callout-title-inner {
|
||||
font-weight: 600;
|
||||
}
|
||||
.callout-fold {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.callout-fold .svg-icon {
|
||||
transition: transform 100ms ease-in-out;
|
||||
}
|
||||
.callout.is-collapsed .callout-fold .svg-icon {
|
||||
transform: rotate(-90deg);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"outputDirectory": "dist",
|
||||
"installCommand": "npm install",
|
||||
"buildCommand": "npm run build",
|
||||
"devCommand": "npm run start",
|
||||
"routes": [
|
||||
{ "handle": "filesystem" },
|
||||
{ "src": "/(.*)", "status": 404, "dest": "/404" }
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue