Implemented support for table of content

This commit is contained in:
Ole Eskild Steensen 2022-12-07 15:30:29 +01:00
parent 8b03626f82
commit 8da3c9317a
12 changed files with 250 additions and 107 deletions

View File

@ -3,6 +3,10 @@ const markdownIt = require("markdown-it");
const fs = require('fs');
const matter = require('gray-matter');
const faviconPlugin = require('eleventy-favicon');
const tocPlugin = require('eleventy-plugin-toc');
const {headerToId, namedHeadingsFilter} = require("./src/helpers/utils")
module.exports = function(eleventyConfig) {
let markdownLib = markdownIt({
@ -184,6 +188,7 @@ module.exports = function(eleventyConfig) {
eleventyConfig.addPassthroughCopy("src/site/img");
eleventyConfig.addPlugin(faviconPlugin, { destination: 'dist' });
eleventyConfig.addPlugin(tocPlugin, {ul:true, tags: ['h1','h2', 'h3', 'h4', 'h5', 'h6']});
eleventyConfig.addFilter('jsonify', function (variable) {
return JSON.stringify(variable);
});
@ -201,47 +206,3 @@ module.exports = function(eleventyConfig) {
};
};
function headerToId(heading) {
return slugify(heading);
}
//https://github.com/rstacruz/markdown-it-named-headings/blob/master/index.js
function namedHeadingsFilter(md, options) {
md.core.ruler.push('named_headings', namedHeadings.bind(null, md));
}
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
}
}

17
package-lock.json generated
View File

@ -15,6 +15,7 @@
"axios": "^0.26.1",
"dotenv": "^10.0.0",
"eleventy-favicon": "^1.1.2",
"eleventy-plugin-toc": "^1.1.5",
"fs-file-tree": "^1.1.1",
"gray-matter": "^4.0.3",
"lunr": "^2.3.9",
@ -1729,6 +1730,14 @@
"to-ico": "^1.1.5"
}
},
"node_modules/eleventy-plugin-toc": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/eleventy-plugin-toc/-/eleventy-plugin-toc-1.1.5.tgz",
"integrity": "sha512-Fo5AZZSBH8CKvz0axJQA9nmnTFOflAMFrngaKER4rOz3C6oDwqxK8N+kNFepmIsieTPkrH+iREWLJ+/9j5JjUg==",
"dependencies": {
"cheerio": "^1.0.0-rc.10"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -7649,6 +7658,14 @@
"to-ico": "^1.1.5"
}
},
"eleventy-plugin-toc": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/eleventy-plugin-toc/-/eleventy-plugin-toc-1.1.5.tgz",
"integrity": "sha512-Fo5AZZSBH8CKvz0axJQA9nmnTFOflAMFrngaKER4rOz3C6oDwqxK8N+kNFepmIsieTPkrH+iREWLJ+/9j5JjUg==",
"requires": {
"cheerio": "^1.0.0-rc.10"
}
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",

View File

@ -28,6 +28,7 @@
"axios": "^0.26.1",
"dotenv": "^10.0.0",
"eleventy-favicon": "^1.1.2",
"eleventy-plugin-toc": "^1.1.5",
"fs-file-tree": "^1.1.1",
"gray-matter": "^4.0.3",
"lunr": "^2.3.9",

View File

@ -5,5 +5,6 @@ exports.ALL_NOTE_SETTINGS= [
"dgShowLocalGraph",
"dgShowInlineTitle",
"dgShowFileTree",
"dgEnableSearch"
"dgEnableSearch",
"dgShowToc"
];

47
src/helpers/utils.js Normal file
View File

@ -0,0 +1,47 @@
const slugify = require("@sindresorhus/slugify");
function headerToId(heading) {
return slugify(heading);
}
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;

View File

@ -117,7 +117,7 @@
ctx.textBaseline = 'top';
ctx.fillText(label, node.x, node.y + nodeR + 2);
})
.linkColor(() => getCssVar("--text-normal"))
.linkColor(() => getCssVar("--text-muted") || getCssVar("--text-normal"))
.graphData(gData)
.onNodeClick(node => {
window.location = node.url;

View File

@ -5,32 +5,53 @@
{%if settings.dgShowLocalGraph === true%}
<div class="graph">
<div style="display: flex; justify-content: flex-end;">
<div class="graph-title">Interactive graph</div>
<div class="graph-title">Connected Pages</div>
</div>
<div id="link-graph"></div>
</div>
{%endif%}
{%endif%}
{%if settings.dgShowBacklinks === true %}
<div class="backlinks">
<div class="backlink-title" style="margin: 4px 0 !important;">Links to this page</div>
{%- if backlinks.length === 0 -%}
<div class="backlink-card">
No backlinks
{%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%}
{%- for backlink in backlinks -%}
<div class="backlink-card">
<a href="{{backlink.url}}">{{backlink.title}}</a>
{%endif%}
{%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 backlinks.length === 0 -%}
<div class="backlink-card">
<span class="no-backlinks-message">No other pages mentions this page</span>
</div>
{%- endif -%}
{%- for backlink in backlinks -%}
<div class="backlink-card">
<i class="fa fa-link"></i> <a href="{{backlink.url}}">{{backlink.title}}</a>
</div>
{%- endfor -%}
</div>
</div>
{%- endfor -%}
{%endif%}
</div>
</div>
{%endif%}
</div>
</div>
</div>
</div>
{%if settings.dgShowLocalGraph === true %}
{%include "components/graphScript.njk"%}
{% endif %}
{%if settings.dgShowLocalGraph === true %}
{%include "components/graphScript.njk"%}
{% endif %}

View File

@ -27,7 +27,7 @@ permalink: "notes/{{ page.fileSlug | slugify }}/"
{% endif %}
{{ content | link | highlight | safe}}
{% if settings.dgShowBacklinks === true or settings.dgShowLocalGraph === true%}
{% if settings.dgShowBacklinks === true or settings.dgShowLocalGraph === true or settings.dgShowToc === true%}
{%include "components/sidebar.njk"%}
{% endif %}
</div>

View File

@ -4,6 +4,11 @@ const settings = require("../helpers/constants");
const wikilink = /\[\[(.*?\|.*?)\]\]/g
const markdownIt = require("markdown-it");
const md = markdownIt({
html: true,
}).use(require("../helpers/utils").namedHeadingsFilter);
function caselessCompare(a, b) {
return a.toLowerCase() === b.toLowerCase();
}
@ -111,6 +116,13 @@ module.exports = {
return currentnote.data.page.fileSlug;
}
return "";
},
content: (data) => {
const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0];
if (currentnote && currentnote.template && currentnote.template.frontMatter && currentnote.template.frontMatter.content) {
return md.render(currentnote.template.frontMatter.content);
}
return "";
}
}
}

View File

@ -24,7 +24,7 @@
{%- endfor -%}
{%if settings.dgShowBacklinks === true or settings.dgShowLocalGraph === true%}
{% if settings.dgShowBacklinks === true or settings.dgShowLocalGraph === true or settings.dgShowToc === true%}
{%include "components/sidebar.njk" %}
{%endif%}
</div>

View File

@ -99,6 +99,6 @@ module.exports = {
noteSettings[setting] = settingValue;
});
return noteSettings;
}
}
}
}

View File

@ -6,6 +6,7 @@
body {
overflow-x: hidden;
}
.content {
max-width: 800px;
margin: auto;
@ -72,14 +73,13 @@ ul.task-list {
.sidebar {
position: fixed;
top: 50%;
transform: translateY(-50%);
transform: translateY(calc(-50% + 75px));
right: 0;
height: 100%;
min-width: 25px;
display: flex;
z-index: 3;
max-width: 350px;
margin-top: 75px;
}
.expand-line {
@ -92,11 +92,67 @@ ul.task-list {
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;
}
.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 {
margin-top: 50px;
background-color: var(--background-secondary);
flex: 1;
margin-top: 10px;
background-color: var(--background-primary);
border-radius: 10px;
padding: 10px;
@ -107,20 +163,37 @@ ul.task-list {
}
}
.backlink-list {
border-left: 1px solid var(--text-accent);
max-height: 200px;
overflow-y: auto;
overflow-x:hidden;
}
.backlink-card {
padding-bottom: 4px;
padding-bottom: 8px;
border-radius: 4px;
width: 100%;
font-size: 1rem;
margin-left: 10px;
color: var(--text-accent);
i {
font-size: 0.8rem;
}
}
.graph{
.graph-title{
.no-backlinks-message{
font-size: 0.8rem;
color: var(--text-normal);
}
.graph {
.graph-title {
width: fit-content;
background-color: var(--background-secondary);
margin: 10px 0 0 0;
padding: 12px;
font-size: 18px !important;
padding: 3px 7px;
font-size: 1.2rem !important;
border-radius: 10px 10px 0 0;
color: var(--h6-color);
}
@ -141,9 +214,10 @@ ul.task-list {
margin-top: 50px;
}
.graph{
.graph {
display: none;
}
.backlinks {
margin-top: 0;
}
@ -152,24 +226,24 @@ ul.task-list {
.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;
padding: 10px;
top: 0px;
left: 0;
position: fixed;
height: 100%;
background-color: var(--background-secondary);
color: var(--text-muted);
overflow-y: auto;
width: 250px;
}
.empty-navbar {
display: flex;
justify-content: flex-end;
display: flex;
justify-content: flex-end;
margin-bottom: -60px;
@media(max-width: 800px) {
justify-content: center;
justify-content: center;
}
.search-button {
@ -191,12 +265,12 @@ ul.task-list {
align-items: center;
.navbar-inner {
display: flex;
align-items: center;
display: flex;
align-items: center;
cursor: pointer;
}
.search-button{
.search-button {
@media(max-width: 800px) {
min-width: 120px;
@ -207,6 +281,11 @@ ul.task-list {
.notelink {
padding: 5px 0 5px 25px;
a{
&:hover {
text-decoration: underline !important;
}
}
}
.foldername-wrapper {
@ -219,31 +298,31 @@ ul.task-list {
}
.filelist {
border-left: 1px solid var(--text-accent);
}
.notelink.active-note{
a{
.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;
.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);
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
right: 0;
@ -279,6 +358,7 @@ ul.task-list {
color: var(--text-primary);
}
.search-box input:focus {
outline: none;
}
@ -317,6 +397,7 @@ ul.task-list {
color: var(--text-secondary);
margin-right: 20px;
}
.search-link {
display: block;
margin-bottom: 4px;
@ -333,14 +414,16 @@ ul.task-list {
margin: 10px 65px;
border: 1px solid var(--text-normal);
cursor: pointer;
>span{
>span {
padding: 3px 3px 3px 10px;
}
>i{
>i {
margin-left: 10px;
}
&:hover{
&:hover {
border: 1px solid var(--text-accent);
}