diff --git a/src/extensions/outliner/client.mjs b/src/extensions/outliner/client.mjs
index fe26044..e318094 100644
--- a/src/extensions/outliner/client.mjs
+++ b/src/extensions/outliner/client.mjs
@@ -7,6 +7,22 @@
"use strict";
+function Heading({ indent, ...props }, ...children) {
+ const { html } = globalThis.__enhancerApi;
+ return html`
+ ${children}
+
`;
+}
+
export default async (api, db) => {
const { html, debounce, addMutationListener, addPanelView } = api,
behavior = (await db.get("smoothScrolling")) ? "smooth" : "auto",
@@ -35,7 +51,7 @@ export default async (api, db) => {
`,
$view: html`
Click on a heading to jump to it.
@@ -44,21 +60,6 @@ export default async (api, db) => {
`,
});
- function Heading({ indent, ...props }, ...children) {
- return html`
- ${children}
-
`;
- }
-
let $page;
const updatePage = () => {
if (document.contains($page)) return;
diff --git a/src/extensions/word-counter/client.css b/src/extensions/word-counter/client.css
deleted file mode 100644
index bd44781..0000000
--- a/src/extensions/word-counter/client.css
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * notion-enhancer: word counter
- * (c) 2021 dragonwocky (https://dragonwocky.me/)
- * (https://notion-enhancer.github.io/) under the MIT license
- */
-
-#word-counter--notice {
- color: var(--theme--text_secondary);
- font-size: 14px;
- margin-top: 0;
-}
-
-.word-counter--stat {
- display: block;
- background: var(--theme--ui_interactive-hover);
- color: var(--theme--text);
- font-size: 14px;
- line-height: 1.2;
- border: 1px solid transparent;
- border-radius: 3px;
- padding: 6px 8px;
- cursor: pointer;
- user-select: none;
-}
-.word-counter--stat:focus,
-.word-counter--stat:hover {
- background: transparent;
- border: 1px solid var(--theme--ui_interactive-hover);
-}
-.word-counter--stat:active {
- background: var(--theme--ui_interactive-active);
-}
-
-.word-counter--stat svg {
- display: inline-block;
- height: 1em;
- width: 1em;
- margin: 0 0.4em -2px 0;
- color: var(--theme--icon_secondary);
-}
diff --git a/src/extensions/word-counter/client.mjs b/src/extensions/word-counter/client.mjs
index 30722d3..42e1f25 100644
--- a/src/extensions/word-counter/client.mjs
+++ b/src/extensions/word-counter/client.mjs
@@ -1,108 +1,84 @@
/**
* notion-enhancer: word counter
- * (c) 2021 dragonwocky (https://dragonwocky.me/)
+ * (c) 2024 dragonwocky (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
const humanTime = (mins) => {
- let readable = '';
- if (1 <= mins) {
+ let readable = "";
+ if (1 <= mins || !mins) {
readable += `${Math.floor(mins)} min`;
- if (2 <= mins) readable += 's';
+ if (2 <= mins) readable += "s";
}
const secs = Math.round((mins % 1) * 60);
if (1 <= secs) {
- if (1 <= mins) readable += ' ';
+ if (1 <= mins) readable += " ";
readable += `${secs} sec`;
- if (2 <= secs) readable += 's';
+ if (2 <= secs) readable += "s";
}
return readable;
};
-export default async function ({ web, components }, db) {
- const dbNoticeText = 'Open a page to see its word count.',
- pageNoticeText = 'Click a stat to copy it.',
- $notice = web.html`${dbNoticeText}
`;
-
- const $wordCount = web.html`12`,
- $characterCount = web.html`12`,
- $sentenceCount = web.html`12`,
- $blockCount = web.html`12`,
- $readingTime = web.html`10 mins`,
- $readingTooltip = web.html`${await components.feather('info')}`,
- $speakingTime = web.html`18 secs`,
- $speakingTooltip = web.html`${await components.feather('info')}`,
- $statList = web.render(
- web.html``,
- web.render(web.html``, $wordCount, ' words'),
- web.render(web.html``, $characterCount, ' characters'),
- web.render(web.html``, $sentenceCount, ' sentences'),
- web.render(web.html``, $blockCount, ' blocks'),
- web.render(
- web.html``,
- $readingTooltip,
- $readingTime,
- ' reading time'
- ),
- web.render(
- web.html``,
- $speakingTooltip,
- $speakingTime,
- ' speaking time'
- )
- );
- $statList.querySelectorAll('.word-counter--stat').forEach(($stat) => {
- $stat.addEventListener('click', () => web.copyToClipboard($stat.innerText));
+function Stat(props, ...children) {
+ const { html } = globalThis.__enhancerApi,
+ $stat = html`
+ ${children}
+
`;
+ $stat.addEventListener("click", () => {
+ navigator.clipboard.writeText($stat.innerText);
});
- components.addTooltip($readingTooltip, '**~ 275 wpm**', { offsetDirection: 'left' });
- components.addTooltip($speakingTooltip, '**~ 180 wpm**', { offsetDirection: 'left' });
-
- let viewFocused = false,
- $page;
- await components.addPanelView({
- id: 'b99deb52-6955-43d2-a53b-a31540cd19a5',
- icon: await components.feather('type'),
- title: 'Word Counter',
- $content: web.render(web.html``, $notice, $statList),
- onFocus: () => {
- viewFocused = true;
- $page = document.getElementsByClassName('notion-page-content')[0];
- updateStats();
- },
- onBlur: () => {
- viewFocused = false;
- },
- });
-
- function updateStats() {
- if (!$page) return;
- const words = $page.innerText.split(/[^\w]+/).length;
- $wordCount.innerText = words;
- $characterCount.innerText = $page.innerText.length;
- $sentenceCount.innerText = $page.innerText.split('.').length;
- $blockCount.innerText = $page.querySelectorAll('[data-block-id]').length;
- $readingTime.innerText = humanTime(words / 275);
- $speakingTime.innerText = humanTime(words / 180);
- }
- const pageObserver = () => {
- if (!viewFocused) return;
- if (document.contains($page)) {
- updateStats();
- } else {
- $page = document.getElementsByClassName('notion-page-content')[0];
- if ($page) {
- $notice.innerText = pageNoticeText;
- $statList.style.display = '';
- updateStats();
- } else {
- $notice.innerText = dbNoticeText;
- $statList.style.display = 'none';
- }
- }
- };
- web.addDocumentObserver(pageObserver, [
- '.notion-page-content',
- '.notion-collection_view_page-block',
- ]);
- pageObserver();
+ return $stat;
}
+
+export default async (api, db) => {
+ const { html, debounce, addMutationListener, addPanelView } = api,
+ readingSpeed = await db.get("readingSpeed"),
+ speakingSpeed = await db.get("speakingSpeed"),
+ $wordCount = html`0`,
+ $characterCount = html`0`,
+ $sentenceCount = html`0`,
+ $blockCount = html`0`,
+ $readingTime = html`${humanTime(0)}`,
+ $speakingTime = html`${humanTime(0)}`,
+ page = ".notion-page-content";
+ addPanelView({
+ title: "Word Counter",
+ $icon: "type",
+ $view: html`
+
+ Click on a stat to copy it.
+
+ <${Stat}>${$wordCount} words/>
+ <${Stat}>${$characterCount} characters/>
+ <${Stat}>${$sentenceCount} sentences/>
+ <${Stat}>${$blockCount} blocks/>
+ <${Stat}>${$readingTime} reading time/>
+ <${Stat}>${$speakingTime} speaking time/>
+ `,
+ });
+
+ let $page;
+ const updateStats = debounce(() => {
+ if (!document.contains($page)) $page = document.querySelector(page);
+ if (!$page) return;
+ const text = $page.innerText,
+ words = text.split(/[^\w]+/).length;
+ $wordCount.innerText = words;
+ $characterCount.innerText = text.length;
+ $sentenceCount.innerText = text.split(".").filter((s) => s.trim()).length;
+ $blockCount.innerText = $page.querySelectorAll("[data-block-id]").length;
+ $readingTime.innerText = humanTime(words / readingSpeed);
+ $speakingTime.innerText = humanTime(words / speakingSpeed);
+ });
+ addMutationListener(page, updateStats);
+ updateStats();
+};
diff --git a/src/extensions/word-counter/mod.json b/src/extensions/word-counter/mod.json
index 8b3c8a4..753a8bc 100644
--- a/src/extensions/word-counter/mod.json
+++ b/src/extensions/word-counter/mod.json
@@ -25,5 +25,6 @@
"description": "The average number of words spoken per minute, used in calculating a page's speaking time.",
"value": 180
}
- ]
+ ],
+ "clientScripts": ["client.mjs"]
}