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 }