Refactor editior instance creation and settings management

- Refactor settings management into separate pinia store
- Move creation of editor instances into editorCacheStore
- Read settings directly from the settingsStore instead of passing it from App to Editor to the editor instance
This commit is contained in:
Jonatan Heyman 2025-01-07 16:58:47 +01:00
parent 574dcfdc24
commit f356e3b249
5 changed files with 162 additions and 171 deletions

View File

@ -1,9 +1,10 @@
<script> <script>
import { mapState, mapActions } from 'pinia' import { mapState, mapActions } from 'pinia'
import { mapWritableState } from 'pinia' import { mapWritableState, mapStores } from 'pinia'
import { useHeynoteStore } from "../stores/heynote-store" import { useHeynoteStore } from "../stores/heynote-store"
import { useErrorStore } from "../stores/error-store" import { useErrorStore } from "../stores/error-store"
import { useSettingsStore } from "../stores/settings-store"
import { OPEN_SETTINGS_EVENT, SETTINGS_CHANGE_EVENT } from '@/src/common/constants' import { OPEN_SETTINGS_EVENT, SETTINGS_CHANGE_EVENT } from '@/src/common/constants'
@ -30,9 +31,6 @@
data() { data() {
return { return {
theme: window.heynote.themeMode.initial,
initialTheme: window.heynote.themeMode.initial,
themeSetting: 'system',
development: window.location.href.indexOf("dev=1") !== -1, development: window.location.href.indexOf("dev=1") !== -1,
showSettings: false, showSettings: false,
settings: window.heynote.settings, settings: window.heynote.settings,
@ -40,30 +38,15 @@
}, },
mounted() { mounted() {
window.heynote.themeMode.get().then((mode) => { this.settingsStore.setUp()
this.theme = mode.computed
this.themeSetting = mode.theme
})
const onThemeChange = (theme) => {
this.theme = theme
if (theme === "system") {
document.documentElement.setAttribute("theme", window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light")
} else {
document.documentElement.setAttribute("theme", theme)
}
}
onThemeChange(window.heynote.themeMode.initial)
window.heynote.themeMode.onChange(onThemeChange)
window.heynote.mainProcess.on(SETTINGS_CHANGE_EVENT, (event, settings) => {
this.settings = settings
})
window.heynote.mainProcess.on(OPEN_SETTINGS_EVENT, () => { window.heynote.mainProcess.on(OPEN_SETTINGS_EVENT, () => {
this.showSettings = true this.showSettings = true
}) })
}, },
beforeUnmount() { beforeUnmount() {
window.heynote.themeMode.removeListener() this.settingsStore.tearDown()
}, },
watch: { watch: {
@ -83,6 +66,7 @@
}, },
computed: { computed: {
...mapStores(useSettingsStore),
...mapState(useHeynoteStore, [ ...mapState(useHeynoteStore, [
"currentBufferPath", "currentBufferPath",
"currentBufferName", "currentBufferName",
@ -131,24 +115,6 @@
this.focusEditor() this.focusEditor()
}, },
toggleTheme() {
let newTheme
// when the "system" theme is used, make sure that the first click always results in amn actual theme change
if (this.initialTheme === "light") {
newTheme = this.themeSetting === "system" ? "dark" : (this.themeSetting === "dark" ? "light" : "system")
} else {
newTheme = this.themeSetting === "system" ? "light" : (this.themeSetting === "light" ? "dark" : "system")
}
window.heynote.themeMode.set(newTheme)
this.themeSetting = newTheme
this.$refs.editor.focus()
},
setTheme(theme) {
window.heynote.themeMode.set(theme)
this.themeSetting = theme
},
onSelectLanguage(language) { onSelectLanguage(language) {
this.closeDialog() this.closeDialog()
this.$refs.editor.setLanguage(language) this.$refs.editor.setLanguage(language)
@ -165,18 +131,9 @@
<template> <template>
<div class="container"> <div class="container">
<Editor <Editor
:theme="theme" :theme="settingsStore.theme"
:development="development" :development="development"
:debugSyntaxTree="false" :debugSyntaxTree="false"
:keymap="settings.keymap"
:emacsMetaKey="settings.emacsMetaKey"
:showLineNumberGutter="settings.showLineNumberGutter"
:showFoldGutter="settings.showFoldGutter"
:bracketClosing="settings.bracketClosing"
:fontFamily="settings.fontFamily"
:fontSize="settings.fontSize"
:defaultBlockLanguage="settings.defaultBlockLanguage || 'text'"
:defaultBlockLanguageAutoDetect="settings.defaultBlockLanguageAutoDetect === undefined ? true : settings.defaultBlockLanguageAutoDetect"
:inert="editorInert" :inert="editorInert"
class="editor" class="editor"
ref="editor" ref="editor"
@ -204,10 +161,10 @@
/> />
<Settings <Settings
v-if="showSettings" v-if="showSettings"
:initialSettings="settings" :initialSettings="settingsStore.settings"
:themeSetting="themeSetting" :themeSetting="settingsStore.themeSetting"
@closeSettings="closeSettings" @closeSettings="closeSettings"
@setTheme="setTheme" @setTheme="settingsStore.setTheme"
/> />
<NewBuffer <NewBuffer
v-if="showCreateBuffer" v-if="showCreateBuffer"

View File

@ -2,7 +2,7 @@
import { HeynoteEditor } from '../editor/editor.js' import { HeynoteEditor } from '../editor/editor.js'
import { syntaxTree } from "@codemirror/language" import { syntaxTree } from "@codemirror/language"
import { toRaw } from 'vue'; import { toRaw } from 'vue';
import { mapState, mapWritableState, mapActions } from 'pinia' import { mapState, mapWritableState, mapActions, mapStores } from 'pinia'
import { useErrorStore } from "../stores/error-store" import { useErrorStore } from "../stores/error-store"
import { useHeynoteStore } from "../stores/heynote-store.js" import { useHeynoteStore } from "../stores/heynote-store.js"
import { useEditorCacheStore } from "../stores/editor-cache" import { useEditorCacheStore } from "../stores/editor-cache"
@ -12,33 +12,8 @@
export default { export default {
props: { props: {
theme: String,
development: Boolean, development: Boolean,
debugSyntaxTree: Boolean, debugSyntaxTree: Boolean,
keymap: {
type: String,
default: "default",
},
emacsMetaKey: {
type: String,
default: "alt",
},
showLineNumberGutter: {
type: Boolean,
default: true,
},
showFoldGutter: {
type: Boolean,
default: true,
},
bracketClosing: {
type: Boolean,
default: false,
},
fontFamily: String,
fontSize: Number,
defaultBlockLanguage: String,
defaultBlockLanguageAutoDetect: Boolean,
}, },
components: {}, components: {},
@ -70,7 +45,9 @@
window.heynote.mainProcess.on(WINDOW_CLOSE_EVENT, this.onWindowClose) window.heynote.mainProcess.on(WINDOW_CLOSE_EVENT, this.onWindowClose)
window.heynote.mainProcess.on(REDO_EVENT, this.onRedo) window.heynote.mainProcess.on(REDO_EVENT, this.onRedo)
window.document.addEventListener("currenciesLoaded", this.onCurrenciesLoaded)
// initialize editorCacheStore (sets up watchers for settings changes, propagating them to all editors)
this.editorCacheStore.setUp();
// if debugSyntaxTree prop is set, display syntax tree for debugging // if debugSyntaxTree prop is set, display syntax tree for debugging
if (this.debugSyntaxTree) { if (this.debugSyntaxTree) {
@ -95,7 +72,7 @@
beforeUnmount() { beforeUnmount() {
window.heynote.mainProcess.off(WINDOW_CLOSE_EVENT, this.onWindowClose) window.heynote.mainProcess.off(WINDOW_CLOSE_EVENT, this.onWindowClose)
window.heynote.mainProcess.off(REDO_EVENT, this.onRedo) window.heynote.mainProcess.off(REDO_EVENT, this.onRedo)
window.document.removeEventListener("currenciesLoaded", this.onCurrenciesLoaded) this.editorCacheStore.tearDown();
}, },
watch: { watch: {
@ -103,66 +80,10 @@
//console.log("currentBufferPath changed to", path) //console.log("currentBufferPath changed to", path)
this.loadBuffer(this.currentBufferPath) this.loadBuffer(this.currentBufferPath)
}, },
theme(newTheme) {
this.eachEditor(editor => {
editor.setTheme(newTheme)
})
},
keymap() {
this.eachEditor(editor => {
editor.setKeymap(this.keymap, this.emacsMetaKey)
})
},
emacsMetaKey() {
this.eachEditor(editor => {
editor.setKeymap(this.keymap, this.emacsMetaKey)
})
},
showLineNumberGutter(show) {
this.eachEditor(editor => {
editor.setLineNumberGutter(show)
})
},
showFoldGutter(show) {
this.eachEditor(editor => {
editor.setFoldGutter(show)
})
},
bracketClosing(value) {
this.eachEditor(editor => {
editor.setBracketClosing(value)
})
},
fontFamily() {
this.eachEditor(editor => {
editor.setFont(this.fontFamily, this.fontSize)
})
},
fontSize() {
this.eachEditor(editor => {
editor.setFont(this.fontFamily, this.fontSize)
})
},
defaultBlockLanguage() {
this.eachEditor(editor => {
editor.setDefaultBlockLanguage(this.defaultBlockLanguage, this.defaultBlockLanguageAutoDetect)
})
},
defaultBlockLanguageAutoDetect() {
this.eachEditor(editor => {
editor.setDefaultBlockLanguage(this.defaultBlockLanguage, this.defaultBlockLanguageAutoDetect)
})
},
}, },
computed: { computed: {
...mapStores(useEditorCacheStore),
...mapState(useHeynoteStore, [ ...mapState(useHeynoteStore, [
"currentBufferPath", "currentBufferPath",
"libraryId", "libraryId",
@ -178,42 +99,21 @@
}, },
methods: { methods: {
...mapActions(useErrorStore, ["addError"]),
...mapActions(useEditorCacheStore, ["getEditor", "addEditor", "eachEditor"]),
loadBuffer(path) { loadBuffer(path) {
//console.log("loadBuffer", path) //console.log("loadBuffer", path)
if (this.editor) { if (this.editor) {
this.editor.hide() this.editor.hide()
} }
let cachedEditor = this.getEditor(path) let cachedEditor = this.editorCacheStore.getEditor(path)
if (cachedEditor) { if (cachedEditor) {
//console.log("show cached editor") //console.log("show cached editor")
this.editor = cachedEditor this.editor = cachedEditor
toRaw(this.editor).show() toRaw(this.editor).show()
} else { } else {
//console.log("create new editor") //console.log("create new editor")
try { this.editor = this.editorCacheStore.createEditor(path, this.$refs.editor)
this.editor = new HeynoteEditor({ this.editorCacheStore.addEditor(path, toRaw(this.editor))
element: this.$refs.editor,
path: path,
theme: this.theme,
keymap: this.keymap,
emacsMetaKey: this.emacsMetaKey,
showLineNumberGutter: this.showLineNumberGutter,
showFoldGutter: this.showFoldGutter,
bracketClosing: this.bracketClosing,
fontFamily: this.fontFamily,
fontSize: this.fontSize,
defaultBlockToken: this.defaultBlockLanguage,
defaultBlockAutoDetect: this.defaultBlockLanguageAutoDetect,
})
} catch (e) {
this.addError("Error! " + e.message)
throw e
}
this.addEditor(path, toRaw(this.editor))
} }
this.currentEditor = toRaw(this.editor) this.currentEditor = toRaw(this.editor)
@ -236,12 +136,6 @@
editor.focus() editor.focus()
}, },
onCurrenciesLoaded() {
if (this.editor) {
toRaw(this.editor).currenciesLoaded()
}
},
focus() { focus() {
toRaw(this.editor).focus() toRaw(this.editor).focus()
}, },

View File

@ -330,8 +330,8 @@ export class HeynoteEditor {
} }
setDefaultBlockLanguage(token, autoDetect) { setDefaultBlockLanguage(token, autoDetect) {
this.defaultBlockToken = token this.defaultBlockToken = token || "text"
this.defaultBlockAutoDetect = autoDetect this.defaultBlockAutoDetect = autoDetect === undefined ? true : autoDetect
} }
formatCurrentBlock() { formatCurrentBlock() {

View File

@ -1,7 +1,11 @@
import { toRaw } from 'vue'; import { toRaw, watch } from 'vue';
import { defineStore } from "pinia" import { defineStore } from "pinia"
import { NoteFormat } from "../common/note-format" import { NoteFormat } from "../common/note-format"
import { useSettingsStore } from './settings-store'
import { useErrorStore } from './error-store'
import { HeynoteEditor } from '../editor/editor'
const NUM_EDITOR_INSTANCES = 5 const NUM_EDITOR_INSTANCES = 5
export const useEditorCacheStore = defineStore("editorCache", { export const useEditorCacheStore = defineStore("editorCache", {
@ -9,10 +13,36 @@ export const useEditorCacheStore = defineStore("editorCache", {
editorCache: { editorCache: {
lru: [], lru: [],
cache: {}, cache: {},
watchHandler: null,
themeWatchHandler: null,
}, },
}), }),
actions: { actions: {
createEditor(path, element) {
const settingsStore = useSettingsStore()
const errorStore = useErrorStore()
try {
return new HeynoteEditor({
element: element,
path: path,
theme: settingsStore.theme,
keymap: settingsStore.settings.keymap,
emacsMetaKey: settingsStore.settings.emacsMetaKey,
showLineNumberGutter: settingsStore.settings.showLineNumberGutter,
showFoldGutter: settingsStore.settings.showFoldGutter,
bracketClosing: settingsStore.settings.bracketClosing,
fontFamily: settingsStore.settings.fontFamily,
fontSize: settingsStore.settings.fontSize,
defaultBlockToken: settingsStore.settings.defaultBlockLanguage,
defaultBlockAutoDetect: settingsStore.settings.defaultBlockLanguageAutoDetect,
})
} catch (e) {
errorStore.addError("Error! " + e.message)
throw e
}
},
getEditor(path) { getEditor(path) {
// move to end of LRU // move to end of LRU
this.editorCache.lru = this.editorCache.lru.filter(p => p !== path) this.editorCache.lru = this.editorCache.lru.filter(p => p !== path)
@ -53,5 +83,66 @@ export const useEditorCacheStore = defineStore("editorCache", {
this.editorCache.cache = {} this.editorCache.cache = {}
this.editorCache.lru = [] this.editorCache.lru = []
}, },
onCurrenciesLoaded() {
this.eachEditor((editor) => {
editor.currenciesLoaded()
})
},
setUp() {
const settingsStore = useSettingsStore()
this.watchHandler = watch(() => settingsStore.settings, (newSettings, oldSettings) => {
//console.log("Settings changed (watch)", newSettings, oldSettings)
const changedKeys = Object.keys(newSettings).filter(key => newSettings[key] !== oldSettings[key])
for (const key of changedKeys) {
this.eachEditor((editor) => {
switch (key) {
case "keymap":
case "emacsMetaKey":
editor.setKeymap(newSettings.keymap, newSettings.emacsMetaKey)
break
case "showLineNumberGutter":
editor.setLineNumberGutter(newSettings.showLineNumberGutter)
break
case "showFoldGutter":
editor.setFoldGutter(newSettings.showFoldGutter)
break
case "bracketClosing":
editor.setBracketClosing(newSettings.bracketClosing)
break
case "fontFamily":
case "fontSize":
editor.setFont(newSettings.fontFamily, newSettings.fontSize)
break
case "defaultBlockLanguage":
case "defaultBlockLanguageAutoDetect":
editor.setDefaultBlockLanguage(newSettings.defaultBlockLanguage, newSettings.defaultBlockLanguageAutoDetect)
break
}
})
}
})
this.themeWatchHandler = watch(() => settingsStore.theme, (theme) => {
this.eachEditor((editor) => {
editor.setTheme(theme)
})
})
window.document.addEventListener("currenciesLoaded", this.onCurrenciesLoaded)
},
tearDown() {
if (this.watchHandler) {
this.watchHandler()
}
if (this.themeWatchHandler) {
this.themeWatchHandler()
}
window.document.removeEventListener("currenciesLoaded", this.onCurrenciesLoaded)
},
}, },
}) })

View File

@ -0,0 +1,49 @@
import { defineStore } from "pinia"
import { SETTINGS_CHANGE_EVENT } from '@/src/common/constants'
export const useSettingsStore = defineStore("settings", {
state: () => {
return {
settings: window.heynote.settings,
themeSetting: "system",
theme: window.heynote.themeMode.initial,
}
},
actions: {
onSettingsChange(settings) {
this.settings = settings
},
setTheme(theme) {
window.heynote.themeMode.set(theme)
this.themeSetting = theme
},
setUp() {
window.heynote.mainProcess.on(SETTINGS_CHANGE_EVENT, (event, settings) => {
this.onSettingsChange(settings)
})
window.heynote.themeMode.get().then((mode) => {
this.theme = mode.computed
this.themeSetting = mode.theme
})
const onThemeChange = (theme) => {
this.theme = theme
if (theme === "system") {
document.documentElement.setAttribute("theme", window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light")
} else {
document.documentElement.setAttribute("theme", theme)
}
}
onThemeChange(window.heynote.themeMode.initial)
window.heynote.themeMode.onChange(onThemeChange)
},
tearDown() {
window.heynote.themeMode.removeListener()
}
},
})