mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-18 09:35:20 +02:00
98 lines
2.1 KiB
TypeScript
98 lines
2.1 KiB
TypeScript
|
import joplin from 'api';
|
||
|
|
||
|
const nodeSlug = require('slug');
|
||
|
|
||
|
// From https://stackoverflow.com/a/6234804/561309
|
||
|
function escapeHtml(unsafe:string) {
|
||
|
return unsafe
|
||
|
.replace(/&/g, "&")
|
||
|
.replace(/</g, "<")
|
||
|
.replace(/>/g, ">")
|
||
|
.replace(/"/g, """)
|
||
|
.replace(/'/g, "'");
|
||
|
}
|
||
|
|
||
|
function noteHeaders(noteBody:string) {
|
||
|
const headers = [];
|
||
|
const lines = noteBody.split('\n');
|
||
|
for (const line of lines) {
|
||
|
const match = line.match(/^(#+)\s(.*)*/);
|
||
|
if (!match) continue;
|
||
|
headers.push({
|
||
|
level: match[1].length,
|
||
|
text: match[2],
|
||
|
});
|
||
|
}
|
||
|
return headers;
|
||
|
}
|
||
|
|
||
|
let slugs:any = {};
|
||
|
|
||
|
function headerSlug(headerText:string) {
|
||
|
const s = nodeSlug(headerText);
|
||
|
let num = slugs[s] ? slugs[s] : 1;
|
||
|
const output = [s];
|
||
|
if (num > 1) output.push(num);
|
||
|
slugs[s] = num + 1;
|
||
|
return output.join('-');
|
||
|
}
|
||
|
|
||
|
joplin.plugins.register({
|
||
|
onStart: async function() {
|
||
|
const panels = joplin.views.panels;
|
||
|
|
||
|
const view = await panels.create();
|
||
|
|
||
|
await panels.setHtml(view, 'Loading...');
|
||
|
await panels.addScript(view, './webview.js');
|
||
|
await panels.addScript(view, './webview.css');
|
||
|
|
||
|
panels.onMessage(view, (message:any) => {
|
||
|
if (message.name === 'scrollToHash') {
|
||
|
joplin.commands.execute('scrollToHash', {
|
||
|
hash: message.hash,
|
||
|
})
|
||
|
}
|
||
|
});
|
||
|
|
||
|
async function updateTocView() {
|
||
|
const note = await joplin.workspace.selectedNote();
|
||
|
slugs = {};
|
||
|
|
||
|
if (note) {
|
||
|
const headers = noteHeaders(note.body);
|
||
|
|
||
|
const itemHtml = [];
|
||
|
for (const header of headers) {
|
||
|
const slug = headerSlug(header.text);
|
||
|
|
||
|
itemHtml.push(`
|
||
|
<p class="toc-item" style="padding-left:${(header.level - 1) * 15}px">
|
||
|
<a class="toc-item-link" href="#" data-slug="${escapeHtml(slug)}">
|
||
|
${escapeHtml(header.text)}
|
||
|
</a>
|
||
|
</p>
|
||
|
`);
|
||
|
}
|
||
|
|
||
|
await panels.setHtml(view, `
|
||
|
<div class="container">
|
||
|
${itemHtml.join('\n')}
|
||
|
</div>
|
||
|
`);
|
||
|
} else {
|
||
|
await panels.setHtml(view, 'Please select a note to view the table of content');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
joplin.workspace.onNoteSelectionChange(() => {
|
||
|
updateTocView();
|
||
|
});
|
||
|
|
||
|
joplin.workspace.onNoteContentChange(() => {
|
||
|
updateTocView();
|
||
|
});
|
||
|
|
||
|
updateTocView();
|
||
|
},
|
||
|
});
|