diff --git a/src/components/App.vue b/src/components/App.vue
index 01e98d7..fb9ad86 100644
--- a/src/components/App.vue
+++ b/src/components/App.vue
@@ -1,12 +1,13 @@
-
-
+
diff --git a/src/components/Editor.vue b/src/components/Editor.vue
index b060942..3454dda 100644
--- a/src/components/Editor.vue
+++ b/src/components/Editor.vue
@@ -10,6 +10,7 @@
Welcome to Heynote!
[${modChar} + Enter] Insert new note block
+[${modChar} + L] Change block language
[${modChar} + Down] Goto next block
[${modChar} + Up] Goto previous block
[${modChar} + A] Select all text in a note block. Press again to select the whole scratchpad
@@ -32,6 +33,10 @@ Welcome to Heynote!
})
})
+ this.$refs.editor.addEventListener("openLanguageSelector", (e) => {
+ this.$emit("openLanguageSelector")
+ })
+
this.editor = new HeynoteEditor({
element: this.$refs.editor,
content: this.development ? testContent : initialContent,
@@ -44,6 +49,21 @@ Welcome to Heynote!
this.editor.setTheme(newTheme)
},
},
+
+ methods: {
+ setLanguage(language) {
+ if (language === "auto") {
+ this.editor.setCurrentLanguage("text", true)
+ } else {
+ this.editor.setCurrentLanguage(language, false)
+ }
+ this.editor.focus()
+ },
+
+ focus() {
+ this.editor.focus()
+ },
+ },
}
diff --git a/src/components/LanguageSelector.vue b/src/components/LanguageSelector.vue
new file mode 100644
index 0000000..c3c03b4
--- /dev/null
+++ b/src/components/LanguageSelector.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
diff --git a/src/components/StatusBar.vue b/src/components/StatusBar.vue
index 2c06013..2d00999 100644
--- a/src/components/StatusBar.vue
+++ b/src/components/StatusBar.vue
@@ -35,7 +35,10 @@
Col {{ column }}
+
{{ languageName }}
(auto)
@@ -61,6 +64,7 @@
padding-right: 0px
display: flex
flex-direction: row
+ align-items: center
user-select: none
+dark-mode
diff --git a/src/css/application.sass b/src/css/application.sass
index aca682d..0c9634f 100644
--- a/src/css/application.sass
+++ b/src/css/application.sass
@@ -1,2 +1,3 @@
+@import "reset"
@import "font"
@import "base"
diff --git a/src/css/base.sass b/src/css/base.sass
index 8c5074f..8225fba 100644
--- a/src/css/base.sass
+++ b/src/css/base.sass
@@ -3,7 +3,7 @@ html, body
padding: 0
background: #fff
color: #444
- font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif
+ font-family: "Open Sans"
height: 100%
font-size: 12px
overscroll-behavior-y: none
diff --git a/src/css/reset.sass b/src/css/reset.sass
new file mode 100644
index 0000000..bd5e747
--- /dev/null
+++ b/src/css/reset.sass
@@ -0,0 +1,31 @@
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video
+ margin: 0
+ padding: 0
+ border: 0
+ font-size: 100%
+ font: inherit
+ vertical-align: baseline
+
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section
+ display: block
+
+body
+ line-height: 1
+
+ol, ul
+ list-style: none
+
+blockquote, q
+ quotes: none
+
+blockquote:before, blockquote:after, q:before, q:after
+ content: ''
+ content: none
+
+table
+ border-collapse: collapse
+ border-spacing: 0
+
+input
+ font-size: 1em
diff --git a/src/editor/block/block.js b/src/editor/block/block.js
index 5746f4b..7a67c2c 100644
--- a/src/editor/block/block.js
+++ b/src/editor/block/block.js
@@ -276,7 +276,7 @@ const blockLineNumbers = lineNumbers({
}
})
-const emitCursorChange = (element) => ViewPlugin.fromClass(
+const emitCursorChange = (editor) => ViewPlugin.fromClass(
class {
update(update) {
// if the selection changed or the language changed (can happen without selection change),
@@ -285,7 +285,7 @@ const emitCursorChange = (element) => ViewPlugin.fromClass(
if (update.selectionSet || langChange) {
const cursorLine = getBlockLineFromPos(update.state, update.state.selection.main.head)
const block = getActiveNoteBlock(update.state)
- element.dispatchEvent(new SelectionChangeEvent({
+ editor.element.dispatchEvent(new SelectionChangeEvent({
cursorLine,
language: block?.language.name,
languageAuto: block?.language.auto,
@@ -295,7 +295,7 @@ const emitCursorChange = (element) => ViewPlugin.fromClass(
}
)
-export const noteBlockExtension = (element) => {
+export const noteBlockExtension = (editor) => {
return [
blockState,
noteBlockWidget(),
@@ -304,6 +304,6 @@ export const noteBlockExtension = (element) => {
preventFirstBlockFromBeingDeleted,
preventSelectionBeforeFirstBlock,
blockLineNumbers,
- emitCursorChange(element),
+ emitCursorChange(editor),
]
}
diff --git a/src/editor/block/commands.js b/src/editor/block/commands.js
index 7e80573..59ea6e5 100644
--- a/src/editor/block/commands.js
+++ b/src/editor/block/commands.js
@@ -76,6 +76,11 @@ export function changeLanguageTo(state, dispatch, block, language, auto) {
}
}
+export function changeCurrentBlockLanguage(state, dispatch, language, auto) {
+ const block = getActiveNoteBlock(state)
+ changeLanguageTo(state, dispatch, block, language, auto)
+}
+
export function gotoPreviousBlock({state, dispatch}) {
const blocks = state.facet(blockState)
const newSelection = EditorSelection.create(state.selection.ranges.map(sel => {
diff --git a/src/editor/editor.js b/src/editor/editor.js
index a4c2577..8eb2ec5 100644
--- a/src/editor/editor.js
+++ b/src/editor/editor.js
@@ -8,18 +8,20 @@ import { heynoteBase } from "./theme/base.js"
import { customSetup } from "./setup.js"
import { heynoteLang } from "./lang-heynote/heynote.js"
import { noteBlockExtension } from "./block/block.js"
+import { changeCurrentBlockLanguage } from "./block/commands.js"
import { heynoteKeymap } from "./keymap.js"
import { languageDetection } from "./language-detection/autodetect.js"
export class HeynoteEditor {
constructor({element, content, focus=true, theme="light"}) {
+ this.element = element
this.theme = new Compartment
- this.state = EditorState.create({
+ const state = EditorState.create({
doc: content || "",
extensions: [
- heynoteKeymap,
+ heynoteKeymap(this),
//minimalSetup,
customSetup,
@@ -31,7 +33,7 @@ export class HeynoteEditor {
return {top: 80, bottom: 80}
}),
heynoteLang(),
- noteBlockExtension(element),
+ noteBlockExtension(this),
languageDetection(() => this.view),
// set cursor blink rate to 1 second
@@ -45,7 +47,7 @@ export class HeynoteEditor {
})
this.view = new EditorView({
- state: this.state,
+ state: state,
parent: element,
})
@@ -58,11 +60,23 @@ export class HeynoteEditor {
}
}
+ focus() {
+ this.view.focus()
+ }
+
setTheme(theme) {
this.view.dispatch({
effects: this.theme.reconfigure(theme === "dark" ? heynoteDark : heynoteLight),
})
}
+
+ openLanguageSelector() {
+ this.element.dispatchEvent(new Event("open-language-selector"))
+ }
+
+ setCurrentLanguage(lang, auto=false) {
+ changeCurrentBlockLanguage(this.view.state, this.view.dispatch, lang, auto)
+ }
}
diff --git a/src/editor/keymap.js b/src/editor/keymap.js
index a0b483d..42a25e9 100644
--- a/src/editor/keymap.js
+++ b/src/editor/keymap.js
@@ -2,41 +2,48 @@ import { keymap } from "@codemirror/view"
import { indentWithTab, insertTab, indentLess, indentMore } from "@codemirror/commands"
import { insertNewNote, moveLineUp, selectAll, gotoPreviousBlock, gotoNextBlock } from "./block/commands.js";
-export const heynoteKeymap = keymap.of([
- {
- key: "Tab",
- preventDefault: true,
- //run: insertTab,
- run: indentMore,
- },
- {
- key: 'Shift-Tab',
- preventDefault: true,
- run: indentLess,
- },
- {
- key: "Mod-Enter",
- preventDefault: true,
- run: insertNewNote,
- },
- {
- key: "Mod-a",
- preventDefault: true,
- run: selectAll,
- },
- {
- key: "Alt-ArrowUp",
- preventDefault: true,
- run: moveLineUp,
- },
- {
- key: "Mod-ArrowUp",
- preventDefault: true,
- run: gotoPreviousBlock,
- },
- {
- key: "Mod-ArrowDown",
- preventDefault: true,
- run: gotoNextBlock,
- },
-])
+export function heynoteKeymap(editor) {
+ return keymap.of([
+ {
+ key: "Tab",
+ preventDefault: true,
+ //run: insertTab,
+ run: indentMore,
+ },
+ {
+ key: 'Shift-Tab',
+ preventDefault: true,
+ run: indentLess,
+ },
+ {
+ key: "Mod-Enter",
+ preventDefault: true,
+ run: insertNewNote,
+ },
+ {
+ key: "Mod-a",
+ preventDefault: true,
+ run: selectAll,
+ },
+ {
+ key: "Alt-ArrowUp",
+ preventDefault: true,
+ run: moveLineUp,
+ },
+ {
+ key: "Mod-ArrowUp",
+ preventDefault: true,
+ run: gotoPreviousBlock,
+ },
+ {
+ key: "Mod-ArrowDown",
+ preventDefault: true,
+ run: gotoNextBlock,
+ },
+ {
+ key: "Mod-l",
+ preventDefault: true,
+ run: () => editor.openLanguageSelector(),
+ }
+ ])
+}
\ No newline at end of file
diff --git a/src/editor/language-detection/autodetect.js b/src/editor/language-detection/autodetect.js
index 325a31f..2ba35a6 100644
--- a/src/editor/language-detection/autodetect.js
+++ b/src/editor/language-detection/autodetect.js
@@ -11,7 +11,7 @@ const HIGHLIGHTJS_TO_TOKEN = Object.fromEntries(LANGUAGES.map(l => [l.highlightj
export function languageDetection(getView) {
- const previousBlockContent = []
+ const previousBlockContent = {}
let idleCallbackId = null
const detectionWorker = new Worker('langdetect-worker.js?worker');
@@ -50,11 +50,6 @@ export function languageDetection(getView) {
cancelIdleCallback(idleCallbackId)
idleCallbackId = null
}
- if (update.transactions.every(tr => tr.annotations.some(a => a.value == LANGUAGE_CHANGE))) {
- // don't run language detection if the change was triggered by a language change
- //console.log("ignoring check after language change")
- return
- }
idleCallbackId = requestIdleCallback(() => {
idleCallbackId = null
@@ -69,7 +64,12 @@ export function languageDetection(getView) {
break
}
}
- if (block === null || block.language.auto === false) {
+ if (block === null) {
+ return
+ } else if (block.language.auto === false) {
+ // if language is not auto, set it's previousBlockContent to null so that we'll trigger a language detection
+ // immediately if the user changes the language to auto
+ delete previousBlockContent[idx]
return
}