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>
import { mapState, mapActions } from 'pinia'
import { mapWritableState } from 'pinia'
import { mapWritableState, mapStores } from 'pinia'
import { useHeynoteStore } from "../stores/heynote-store"
import { useErrorStore } from "../stores/error-store"
import { useSettingsStore } from "../stores/settings-store"
import { OPEN_SETTINGS_EVENT, SETTINGS_CHANGE_EVENT } from '@/src/common/constants'
@ -30,9 +31,6 @@
data() {
return {
theme: window.heynote.themeMode.initial,
initialTheme: window.heynote.themeMode.initial,
themeSetting: 'system',
development: window.location.href.indexOf("dev=1") !== -1,
showSettings: false,
settings: window.heynote.settings,
@ -40,30 +38,15 @@
},
mounted() {
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)
window.heynote.mainProcess.on(SETTINGS_CHANGE_EVENT, (event, settings) => {
this.settings = settings
})
this.settingsStore.setUp()
window.heynote.mainProcess.on(OPEN_SETTINGS_EVENT, () => {
this.showSettings = true
})
},
beforeUnmount() {
window.heynote.themeMode.removeListener()
this.settingsStore.tearDown()
},
watch: {
@ -83,6 +66,7 @@
},
computed: {
...mapStores(useSettingsStore),
...mapState(useHeynoteStore, [
"currentBufferPath",
"currentBufferName",
@ -131,24 +115,6 @@
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) {
this.closeDialog()
this.$refs.editor.setLanguage(language)
@ -165,18 +131,9 @@
<template>
<div class="container">
<Editor
:theme="theme"
:theme="settingsStore.theme"
:development="development"
: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"
class="editor"
ref="editor"
@ -204,10 +161,10 @@
/>
<Settings
v-if="showSettings"
:initialSettings="settings"
:themeSetting="themeSetting"
:initialSettings="settingsStore.settings"
:themeSetting="settingsStore.themeSetting"
@closeSettings="closeSettings"
@setTheme="setTheme"
@setTheme="settingsStore.setTheme"
/>
<NewBuffer
v-if="showCreateBuffer"

View File

@ -2,7 +2,7 @@
import { HeynoteEditor } from '../editor/editor.js'
import { syntaxTree } from "@codemirror/language"
import { toRaw } from 'vue';
import { mapState, mapWritableState, mapActions } from 'pinia'
import { mapState, mapWritableState, mapActions, mapStores } from 'pinia'
import { useErrorStore } from "../stores/error-store"
import { useHeynoteStore } from "../stores/heynote-store.js"
import { useEditorCacheStore } from "../stores/editor-cache"
@ -12,33 +12,8 @@
export default {
props: {
theme: String,
development: 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: {},
@ -70,7 +45,9 @@
window.heynote.mainProcess.on(WINDOW_CLOSE_EVENT, this.onWindowClose)
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 (this.debugSyntaxTree) {
@ -95,7 +72,7 @@
beforeUnmount() {
window.heynote.mainProcess.off(WINDOW_CLOSE_EVENT, this.onWindowClose)
window.heynote.mainProcess.off(REDO_EVENT, this.onRedo)
window.document.removeEventListener("currenciesLoaded", this.onCurrenciesLoaded)
this.editorCacheStore.tearDown();
},
watch: {
@ -103,66 +80,10 @@
//console.log("currentBufferPath changed to", path)
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: {
...mapStores(useEditorCacheStore),
...mapState(useHeynoteStore, [
"currentBufferPath",
"libraryId",
@ -178,42 +99,21 @@
},
methods: {
...mapActions(useErrorStore, ["addError"]),
...mapActions(useEditorCacheStore, ["getEditor", "addEditor", "eachEditor"]),
loadBuffer(path) {
//console.log("loadBuffer", path)
if (this.editor) {
this.editor.hide()
}
let cachedEditor = this.getEditor(path)
let cachedEditor = this.editorCacheStore.getEditor(path)
if (cachedEditor) {
//console.log("show cached editor")
this.editor = cachedEditor
toRaw(this.editor).show()
} else {
//console.log("create new editor")
try {
this.editor = new HeynoteEditor({
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.editor = this.editorCacheStore.createEditor(path, this.$refs.editor)
this.editorCacheStore.addEditor(path, toRaw(this.editor))
}
this.currentEditor = toRaw(this.editor)
@ -236,12 +136,6 @@
editor.focus()
},
onCurrenciesLoaded() {
if (this.editor) {
toRaw(this.editor).currenciesLoaded()
}
},
focus() {
toRaw(this.editor).focus()
},

View File

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

View File

@ -1,7 +1,11 @@
import { toRaw } from 'vue';
import { toRaw, watch } from 'vue';
import { defineStore } from "pinia"
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
export const useEditorCacheStore = defineStore("editorCache", {
@ -9,10 +13,36 @@ export const useEditorCacheStore = defineStore("editorCache", {
editorCache: {
lru: [],
cache: {},
watchHandler: null,
themeWatchHandler: null,
},
}),
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) {
// move to end of LRU
this.editorCache.lru = this.editorCache.lru.filter(p => p !== path)
@ -53,5 +83,66 @@ export const useEditorCacheStore = defineStore("editorCache", {
this.editorCache.cache = {}
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()
}
},
})