Awesome
Loading scripts from this repository
A few scripts are exported from this repository. To load them within a page, you can use
// For update-rss.js
import("https://cdn.jsdelivr.net/gh/71/logseq-snippets/update-rss.js")
Since you shouldn't trust random scripts you load from the internet, you should specify the hash of the last commit you looked at, e.g.
// For update-rss.js
import("https://cdn.jsdelivr.net/gh/71/logseq-snippets@de5c4a035657dd96a91252d189fb2d0aed6261b3/update-rss.js")
Note that you can't directly link to GitHub (via ?raw=true
), since the MIME type wouldn't be correct.
Fake "Recent" block in Contents.md
This will render a block-like list with all the recently modified pages.
It's a little hacky because Logseq queries cannot return pages, so we must make them look like blocks manually.
@@html: <div style="display:none">@@
#+BEGIN_QUERY
{:title "Recent"
:query [:find (pull ?p [*])
:in $ ?start
:where
[?p :page/last-modified-at ?d]
[?p :page/name ?n]
[(>= ?d ?start)]
[(not= ?n "contents")]]
:inputs [:4d-before]
:view (fn [result]
(for [p (take 4 (sort-by :page/last-modified-at > result))]
[:div.ls-block.flex.flex-col.pt-1
[:div.flex-1.flex-row
[:div.mr-2.flex.flex-row.items-center
{:style {:height "24px" :margin-top "0" :float "left"}}
[:a.block-control.opacity-50.hover:opacity-100
{:style {:min-width "0" :height "16px" :margin-right "2px"}}
[:span]]
[:a
{:href (str "/page/" (:page/name p))}
[:span.bullet-container.cursor
[:span.bullet]]]]
[:div.flex.relative
[:div.flex-1.flex-col.relative.block-content
[:div
[:span.page-reference
[:a.page-ref
{:href (str "/page/" (:page/name p))}
(-> p :page/properties :title)]]]]]]]))}
#+END_QUERY
<style>
.custom-query { margin-top: 0; }
.custom-query .opacity-70 { opacity: 1; }
</style>
The <script-block>
element
This is a WebComponent that executes its content as JavaScript.
First, import it:
<style onload="Function(this.innerHTML.slice(2, this.innerHTML.length - 2))()">/*
import("https://cdn.jsdelivr.net/gh/71/logseq-snippets@main/script-block.js")
*/</style>
Usage is:
<script-block state='{}'><!-- return document.createElement("div"); --></script-block>
Within the JavaScript function, the function has access to:
this
, the current state; can be mutated, and takes the value of thestate
attribute (after parsing from JSON).save
, a function that takes an object and:- Serializes the object to JSON.
- Edits the source of the block so that
<script-block state='...'>
becomes<script-block state='${json}'>
. - Saves the changes to the block.
html
andsvg
from htl.
This element therefore essentially allows you to define custom components based on JavaScript whose state can be mutated and persisted. Since their state is saved to the underlying file, it will also be saved in the Git repository.
For an example, see the counter.
Counter
Increments by one everytime it is clicked.
@@html: <script-block state='{"count":0}'><!-- return html`<button onclick=${() => save({ count: this.count + 1 })}>${this.count ?? 0}`; --></script-block>@@
The <define-script-block>
element
This is a WebComponent used to define reusable <script-block>
elements. For instance, let's implement
the counter again!
Reusable counter
Put this anywhere:
<define-script-block name="x-counter"><!--
return html`
<button onclick=${() => save({ count: +this.count + 1 })}>
${this.count}
`;
--></define-script-block>
And use it like this:
@@html: <x-counter count="0"></x-counter>@@
Alternatively to <define-script-block>
:
<style onload="Function(this.innerHTML.slice(2, this.innerHTML.length - 2))()">/*
import("https://cdn.jsdelivr.net/gh/71/logseq-snippets@main/script-block.js")
.then(({ defineElement }) => defineElement("x-counter", ({ save, html, count }) => html`<a onclick=${() => save({ count: +count + 1 })}>${count}`))
*/</style>
update-rss.js
This script can be loaded in Logseq to automatically update a page named "RSS".
It must contain two top-level items. One must start with "Feeds", and contains "feed descriptions." The other must start with "Items", and will contain the feed items.
For instance:
---
title: RSS
---
- Feeds ( <a onclick="import('https://cdn.jsdelivr.net/gh/71/logseq-snippets/update-rss.js#interval=0')">Refresh</a> )
- [The Pudding](https://pudding.cool/feed/index.xml)
SCHEDULED: <2021-06-24 Thu 11:0 .+1d>
- [XKCD](https://xkcd.com/atom.xml)
SCHEDULED: <2021-06-25 Fri 12:0 .+2d>
- [The Rust Blog](https://blog.rust-lang.org/feed.xml)
SCHEDULED: <2021-06-24 Thu 18:0 .+1d>
<!-- REGEXP: /^Announcing // -->
- Items
- <2021-06-21 00:00> [[XKCD]]: [Houseguests](https://xkcd.com/2479/)
If you use ViolentMonkey, you can load the script on start-up and tell it to refresh its data every e.g. 60,000ms:
// ==UserScript==
// @match https://logseq.com/
// @grant none
// ==/UserScript==
import("https://cdn.jsdelivr.net/gh/71/logseq-snippets/update-rss.js#interval=60000")
Loading it with the force
parameter will reload all feeds, even if their SCHEDULED
time hasn't been reached yet, e.g.
import("https://cdn.jsdelivr.net/gh/71/logseq-snippets/update-rss.js#force")
CORS
To bypass CORS, I use the following ViolentMonkey script:
// ==UserScript==
// @match https://logseq.com/*
// @grant GM_xmlhttpRequest
// @inject-into page
// ==/UserScript==
unsafeWindow.fetchNoCors = (url) => new Promise((resolve, reject) => GM_xmlhttpRequest({
url,
method: 'GET',
onabort: () => reject(),
onerror: () => reject(),
onloadend: (res) => resolve({ async text() { return res.responseText; } }),
}));
Execution scripts
I use the following script to execute JS scripts automatically.
const watchedElements = [];
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.target.classList.contains("extensions__code") &&
mutation.target.firstChild.textContent === "js,run") {
const code = mutation.target.children[1].value,
dispose = Function(code)();
if (typeof dispose === "function") {
watchedElements.push([mutation.target, dispose]);
}
} else if (mutation.removedNodes.length === 1) {
const removedNode = mutation.removedNodes[0];
for (const [watchedElement, dispose] of watchedElements) {
if (removedNode.contains(watchedElement)) {
dispose();
}
}
}
}
});
observer.observe(
document.getElementById("main-content-container"),
{ subtree: true, childList: true },
);
And then:
```js,run
console.log("Script loaded.");
return () => console.log("Script unloaded.");
```