diff --git a/src/components/StatusBar.vue b/src/components/StatusBar.vue
index 4a2ded9a..da139df2 100644
--- a/src/components/StatusBar.vue
+++ b/src/components/StatusBar.vue
@@ -36,6 +36,7 @@
"currentCursorLine",
"currentLanguage",
"currentSelectionSize",
+ "currentSelectionWordCount",
"currentLanguage",
"currentLanguageAuto",
"currentCreatedTime",
@@ -109,6 +110,9 @@
Col {{ currentCursorLine?.col }}
Sel {{ currentSelectionSize }}
+
+ | Words {{ currentSelectionWordCount }}
+
{
(sel) => getSelectionSize(update.state, sel)
).reduce((a, b) => a + b, 0)
+ const selectionWordCount = update.state.selection.ranges.map(
+ (sel) => getSelectionWordCount(update.state, sel)
+ ).reduce((a, b) => a + b, 0)
+
const block = getActiveNoteBlock(update.state)
if (block && cursorLine) {
heynoteStore.currentCursorLine = cursorLine
heynoteStore.currentSelectionSize = selectionSize
+ heynoteStore.currentSelectionWordCount = selectionWordCount
heynoteStore.currentLanguage = block.language.name
heynoteStore.currentLanguageAuto = block.language.auto
heynoteStore.currentBufferName = editor.name
diff --git a/src/stores/heynote-store.js b/src/stores/heynote-store.js
index 7ebe9332..e48fb56b 100644
--- a/src/stores/heynote-store.js
+++ b/src/stores/heynote-store.js
@@ -23,6 +23,7 @@ export const useHeynoteStore = defineStore("heynote", {
currentLanguageAuto: null,
currentCursorLine: null,
currentSelectionSize: null,
+ currentSelectionWordCount: null,
currentCreatedTime: null,
libraryId: 0,
createBufferParams: {
diff --git a/tests/playwright/markdown.spec.js b/tests/playwright/markdown.spec.js
index 3617fa74..79a50496 100644
--- a/tests/playwright/markdown.spec.js
+++ b/tests/playwright/markdown.spec.js
@@ -21,6 +21,56 @@ test("test markdown mode", async ({ page }) => {
await expect(page.locator("css=.status .status-block.lang")).toHaveText("Markdown")
})
+test("selection word count is shown in status bar", async ({ page }) => {
+ await heynotePage.setContent(`
+∞∞∞text
+Hello world test
+`)
+ await page.waitForTimeout(200)
+
+ await page.evaluate(() => {
+ const view = window._heynote_editor.view
+ const text = view.state.doc.toString()
+ const start = text.indexOf("Hello")
+ const end = text.indexOf("test") + "test".length
+ view.dispatch({
+ selection: { anchor: start, head: end },
+ scrollIntoView: true,
+ })
+ })
+
+ await page.waitForTimeout(100)
+ await expect(page.locator("css=.status .status-block.line-number")).toContainText("Words 3")
+})
+
+test("fenced Markdown code blocks are syntax highlighted by language", async ({ page }) => {
+ await heynotePage.setContent(`
+∞∞∞markdown
+\`\`\`js
+class StrictModel {
+ static fields = new Set();
+}
+\`\`\`
+\`\`\`py
+def strict_function():
+ return 1
+\`\`\`
+`)
+ await page.waitForTimeout(300)
+
+ const classToken = await page.locator("css=.cm-content span:has-text('class')").first().evaluate(el => el.className)
+ const strictModelToken = await page.locator("css=.cm-content span:has-text('StrictModel')").first().evaluate(el => el.className)
+ const defToken = await page.locator("css=.cm-content span:has-text('def')").first().evaluate(el => el.className)
+
+ expect(classToken).not.toBe("")
+ expect(strictModelToken).not.toBe("")
+ expect(defToken).not.toBe("")
+
+ // keyword-like tokens in JS and Python should have same style class and differ from identifiers.
+ expect(classToken).toBe(defToken)
+ expect(classToken).not.toBe(strictModelToken)
+})
+
test("checkbox toggle", async ({ page }) => {
await heynotePage.setContent(`
∞∞∞markdown