Top-bottom panel refactor, more instant rendering
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
function processClick(e) {
|
||||
const dataEl = e.target.closest("[data-ref]");
|
||||
syscall(
|
||||
"system.invokeFunction",
|
||||
"index.navigateToMention",
|
||||
dataEl.getAttribute("data-ref"),
|
||||
).catch(console.error);
|
||||
}
|
||||
|
||||
document.getElementById("link-ul").addEventListener("click", processClick);
|
||||
document.getElementById("hide-button").addEventListener("click", () => {
|
||||
syscall("system.invokeFunction", "index.toggleMentions").catch(console.error);
|
||||
});
|
||||
|
||||
document.getElementById("reload-button").addEventListener("click", () => {
|
||||
syscall("system.invokeFunction", "index.renderMentions").catch(
|
||||
console.error,
|
||||
);
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
/* Reset SB styles */
|
||||
html,
|
||||
body {
|
||||
/*height: initial !important;
|
||||
overflow-x: initial !important;
|
||||
overflow-y: hidden !important;*/
|
||||
background-color: var(--root-background-color) !important;
|
||||
}
|
||||
|
||||
#sb-main {
|
||||
height: initial !important;
|
||||
display: initial !important;
|
||||
}
|
||||
|
||||
#sb-editor {
|
||||
flex: initial !important;
|
||||
height: initial !important;
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
height: initial !important;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--ui-font);
|
||||
|
||||
color: var(--root-color);
|
||||
}
|
||||
|
||||
.sb-line-h2 {
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
margin: 0;
|
||||
padding: 10px !important;
|
||||
background-color: rgba(233, 233, 233, 0.5);
|
||||
}
|
||||
|
||||
#button-bar {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
#button-bar button {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
color: var(--root-color);
|
||||
}
|
||||
|
||||
#edit-button {
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
|
||||
li code {
|
||||
font-size: 80%;
|
||||
color: #a5a4a4;
|
||||
}
|
||||
|
||||
li.toc-header-1 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
li.toc-header-2 {
|
||||
margin-left: 2ch;
|
||||
}
|
||||
|
||||
li.toc-header-3 {
|
||||
margin-left: 4ch;
|
||||
}
|
||||
|
||||
li.toc-header-4 {
|
||||
margin-left: 6ch;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
function processClick(e) {
|
||||
const dataEl = e.target.closest("[data-ref]");
|
||||
syscall(
|
||||
"system.invokeFunction",
|
||||
"index.navigateToMention",
|
||||
dataEl.getAttribute("data-ref"),
|
||||
).catch(console.error);
|
||||
}
|
||||
|
||||
document.getElementById("link-ul").addEventListener("click", processClick);
|
||||
document.getElementById("hide-button").addEventListener("click", () => {
|
||||
syscall("system.invokeFunction", "index.toggleTOC").catch(console.error);
|
||||
});
|
||||
|
||||
document.body.addEventListener("mouseenter", () => {
|
||||
console.log("Refreshing on focus");
|
||||
syscall("system.invokeFunction", "index.renderTOC").catch(
|
||||
console.error,
|
||||
);
|
||||
});
|
||||
|
||||
document.getElementById("reload-button").addEventListener("click", () => {
|
||||
syscall("system.invokeFunction", "index.renderTOC").catch(
|
||||
console.error,
|
||||
);
|
||||
});
|
||||
@@ -10,8 +10,6 @@ syntax:
|
||||
- "$"
|
||||
regex: "\\$[a-zA-Z\\.\\-\\/]+[\\w\\.\\-\\/]*"
|
||||
className: sb-named-anchor
|
||||
assets:
|
||||
- asset/*
|
||||
functions:
|
||||
loadBuiltinsIntoIndex:
|
||||
path: builtins.ts:loadBuiltinsIntoIndex
|
||||
@@ -159,20 +157,14 @@ functions:
|
||||
|
||||
# Mentions panel (postscript)
|
||||
toggleMentions:
|
||||
path: "./mentions_ps.ts:toggleMentions"
|
||||
path: "./linked_mentions.ts:toggleMentions"
|
||||
command:
|
||||
name: "Mentions: Toggle"
|
||||
key: ctrl-alt-m
|
||||
updateMentions:
|
||||
path: "./mentions_ps.ts:updateMentions"
|
||||
env: client
|
||||
events:
|
||||
- editor:pageLoaded
|
||||
navigateToMention:
|
||||
path: "./mentions_ps.ts:navigate"
|
||||
|
||||
renderMentions:
|
||||
path: "./mentions_ps.ts:renderMentions"
|
||||
path: "./linked_mentions.ts:renderMentions"
|
||||
panelWidget: bottom
|
||||
|
||||
# TOC
|
||||
toggleTOC:
|
||||
@@ -184,8 +176,7 @@ functions:
|
||||
renderTOC:
|
||||
path: toc.ts:renderTOC
|
||||
env: client
|
||||
events:
|
||||
- editor:pageLoaded
|
||||
panelWidget: top
|
||||
|
||||
lintYAML:
|
||||
path: lint.ts:lintYAML
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { clientStore, editor, system } from "$sb/silverbullet-syscall/mod.ts";
|
||||
import { CodeWidgetContent } from "$sb/types.ts";
|
||||
import { queryObjects } from "./api.ts";
|
||||
import { LinkObject } from "./page_links.ts";
|
||||
|
||||
const hideMentionsKey = "hideMentions";
|
||||
|
||||
export async function toggleMentions() {
|
||||
let hideMentions = await clientStore.get(hideMentionsKey);
|
||||
hideMentions = !hideMentions;
|
||||
await clientStore.set(hideMentionsKey, hideMentions);
|
||||
if (!hideMentions) {
|
||||
await renderMentions();
|
||||
} else {
|
||||
await editor.dispatch({});
|
||||
}
|
||||
}
|
||||
|
||||
export async function renderMentions(): Promise<CodeWidgetContent | null> {
|
||||
const page = await editor.getCurrentPage();
|
||||
const linksResult = await queryObjects<LinkObject>("link", {
|
||||
// Query all links that point to this page, excluding those that are inside directives and self pointers.
|
||||
filter: ["and", ["!=", ["attr", "page"], ["string", page]], ["and", ["=", [
|
||||
"attr",
|
||||
"toPage",
|
||||
], ["string", page]], ["=", ["attr", "inDirective"], ["boolean", false]]]],
|
||||
});
|
||||
if (linksResult.length === 0) {
|
||||
// Don't show the panel if there are no links here.
|
||||
return null;
|
||||
} else {
|
||||
let renderedMd = "# Linked Mentions\n";
|
||||
for (const link of linksResult) {
|
||||
let snippet = await system.invokeFunction(
|
||||
"markdown.markdownToHtml",
|
||||
link.snippet,
|
||||
);
|
||||
// strip HTML tags
|
||||
snippet = snippet.replace(/<[^>]*>?/gm, "");
|
||||
renderedMd += `* [[${link.ref}]]: ...${snippet}...\n`;
|
||||
}
|
||||
return {
|
||||
markdown: renderedMd,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { asset } from "$sb/plugos-syscall/mod.ts";
|
||||
import { clientStore, editor } from "$sb/silverbullet-syscall/mod.ts";
|
||||
import { queryObjects } from "./api.ts";
|
||||
import { LinkObject } from "./page_links.ts";
|
||||
|
||||
const hideMentionsKey = "hideMentions";
|
||||
|
||||
export async function toggleMentions() {
|
||||
let hideMentions = await clientStore.get(hideMentionsKey);
|
||||
hideMentions = !hideMentions;
|
||||
await clientStore.set(hideMentionsKey, hideMentions);
|
||||
if (!hideMentions) {
|
||||
await renderMentions();
|
||||
} else {
|
||||
await editor.hidePanel("bottom");
|
||||
}
|
||||
}
|
||||
|
||||
// Triggered when switching pages or upon first load
|
||||
export async function updateMentions() {
|
||||
if (await clientStore.get(hideMentionsKey)) {
|
||||
return;
|
||||
}
|
||||
await renderMentions();
|
||||
}
|
||||
|
||||
// use internal navigation via syscall to prevent reloading the full page.
|
||||
export async function navigate(ref: string) {
|
||||
const currentPage = await editor.getCurrentPage();
|
||||
const [page, pos] = ref.split(/[@$]/);
|
||||
if (page === currentPage) {
|
||||
await editor.moveCursor(+pos, true);
|
||||
} else {
|
||||
await editor.navigate(page, +pos);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(unsafe: string) {
|
||||
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(
|
||||
/>/g,
|
||||
">",
|
||||
);
|
||||
}
|
||||
|
||||
export async function renderMentions() {
|
||||
const page = await editor.getCurrentPage();
|
||||
const linksResult = await queryObjects<LinkObject>("link", {
|
||||
// Query all links that point to this page, excluding those that are inside directives and self pointers.
|
||||
filter: ["and", ["!=", ["attr", "page"], ["string", page]], ["and", ["=", [
|
||||
"attr",
|
||||
"toPage",
|
||||
], ["string", page]], ["=", ["attr", "inDirective"], ["boolean", false]]]],
|
||||
});
|
||||
if (linksResult.length === 0) {
|
||||
// Don't show the panel if there are no links here.
|
||||
await editor.hidePanel("bottom");
|
||||
} else {
|
||||
const css = await asset.readAsset("asset/style.css");
|
||||
const js = await asset.readAsset("asset/linked_mentions.js");
|
||||
|
||||
await editor.showPanel(
|
||||
"bottom",
|
||||
1,
|
||||
` <style>${css}</style>
|
||||
<div id="sb-main"><div id="sb-editor"><div class="cm-editor">
|
||||
<div id="button-bar">
|
||||
<button id="reload-button" title="Reload"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg></button>
|
||||
<button id="hide-button" title="Hide linked mentions"><svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg></button>
|
||||
</div>
|
||||
<div class="cm-line sb-line-h2">Linked Mentions</div>
|
||||
<ul id="link-ul">
|
||||
${
|
||||
linksResult.map((link) =>
|
||||
`<li data-ref="${link.ref}"><span class="sb-wiki-link-page">${link.ref}</span>: <code>...${
|
||||
escapeHtml(link.snippet)
|
||||
}...</code></li>`
|
||||
).join("")
|
||||
}
|
||||
</ul>
|
||||
</div></div></div>
|
||||
`,
|
||||
js,
|
||||
);
|
||||
}
|
||||
}
|
||||
+19
-39
@@ -5,7 +5,7 @@ import {
|
||||
system,
|
||||
} from "$sb/silverbullet-syscall/mod.ts";
|
||||
import { renderToText, traverseTree, traverseTreeAsync } from "$sb/lib/tree.ts";
|
||||
import { asset } from "$sb/syscalls.ts";
|
||||
import { CodeWidgetContent } from "$sb/types.ts";
|
||||
|
||||
const hideTOCKey = "hideTOC";
|
||||
const headerThreshold = 3;
|
||||
@@ -30,9 +30,11 @@ async function markdownToHtml(text: string): Promise<string> {
|
||||
return system.invokeFunction("markdown.markdownToHtml", text);
|
||||
}
|
||||
|
||||
export async function renderTOC(reload = false) {
|
||||
export async function renderTOC(
|
||||
reload = false,
|
||||
): Promise<CodeWidgetContent | null> {
|
||||
if (await clientStore.get(hideTOCKey)) {
|
||||
return editor.hidePanel("top");
|
||||
return null;
|
||||
}
|
||||
const page = await editor.getCurrentPage();
|
||||
const text = await editor.getText();
|
||||
@@ -41,9 +43,7 @@ export async function renderTOC(reload = false) {
|
||||
await traverseTreeAsync(tree, async (n) => {
|
||||
if (n.type?.startsWith("ATXHeading")) {
|
||||
headers.push({
|
||||
name: await markdownToHtml(
|
||||
n.children!.slice(1).map(renderToText).join("").trim(),
|
||||
),
|
||||
name: n.children!.slice(1).map(renderToText).join("").trim(),
|
||||
pos: n.from!,
|
||||
level: +n.type[n.type.length - 1],
|
||||
});
|
||||
@@ -52,39 +52,19 @@ export async function renderTOC(reload = false) {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// console.log("All headers", headers);
|
||||
if (!reload && cachedTOC === JSON.stringify(headers)) {
|
||||
// TOC is the same, not updating
|
||||
return;
|
||||
}
|
||||
cachedTOC = JSON.stringify(headers);
|
||||
if (headers.length < headerThreshold) {
|
||||
// console.log("Not enough headers, not showing TOC", headers.length);
|
||||
await editor.hidePanel("top");
|
||||
return;
|
||||
console.log("Not enough headers, not showing TOC", headers.length);
|
||||
return null;
|
||||
}
|
||||
const css = await asset.readAsset("asset/style.css");
|
||||
const js = await asset.readAsset("asset/toc.js");
|
||||
|
||||
await editor.showPanel(
|
||||
"top",
|
||||
1,
|
||||
` <style>${css}</style>
|
||||
<div id="sb-main"><div id="sb-editor"><div class="cm-editor">
|
||||
<div id="button-bar">
|
||||
<button id="reload-button" title="Reload"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg></button>
|
||||
<button id="hide-button" title="Hide TOC"><svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg></button>
|
||||
</div>
|
||||
<div class="cm-line sb-line-h2">Table of Contents</div>
|
||||
<ul id="link-ul">
|
||||
${
|
||||
headers.map((header) =>
|
||||
`<li data-ref="${page}@${header.pos}" class="toc-header-${header.level}"><span class="sb-wiki-link-page">${header.name}</span></li>`
|
||||
).join("")
|
||||
}
|
||||
</ul>
|
||||
</div></div></div>
|
||||
`,
|
||||
js,
|
||||
);
|
||||
// console.log("Headers", headers);
|
||||
const renderedMd = "# Table of Contents\n" +
|
||||
headers.map((header) =>
|
||||
`${
|
||||
" ".repeat((header.level - 1) * 2)
|
||||
}* [[${page}@${header.pos}|${header.name}]]`
|
||||
).join("\n");
|
||||
// console.log("Markdown", renderedMd);
|
||||
return {
|
||||
markdown: renderedMd,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user