mirror of
https://github.com/heyman/heynote.git
synced 2024-12-31 19:20:21 +01:00
WIP: Implement ability to create new notes.
Support cache of multiple Editor instances. Change so that current note name is included in the event data dispatched by emitCursorChange.
This commit is contained in:
parent
5e34656c1d
commit
5b61a0a234
@ -64,6 +64,14 @@ export class FileLibrary {
|
||||
return await this.files[path].save(content)
|
||||
}
|
||||
|
||||
async create(path, content) {
|
||||
if (await this.exists(path)) {
|
||||
throw new Error(`File already exists: ${path}`)
|
||||
}
|
||||
const fullPath = join(this.basePath, path)
|
||||
await this.jetpack.writeAsync(fullPath, content)
|
||||
}
|
||||
|
||||
async getList() {
|
||||
console.log("Loading notes")
|
||||
const notes = {}
|
||||
@ -194,6 +202,10 @@ export function setupFileLibraryEventHandlers(library, win) {
|
||||
return await library.save(path, content)
|
||||
});
|
||||
|
||||
ipcMain.handle('buffer:create', async (event, path, content) => {
|
||||
return await library.create(path, content)
|
||||
});
|
||||
|
||||
ipcMain.handle('buffer:getList', async (event) => {
|
||||
return await library.getList()
|
||||
});
|
||||
|
@ -77,6 +77,10 @@ contextBridge.exposeInMainWorld("heynote", {
|
||||
return await ipcRenderer.invoke("buffer:save", path, content)
|
||||
},
|
||||
|
||||
async create(path, content) {
|
||||
return await ipcRenderer.invoke("buffer:create", path, content)
|
||||
},
|
||||
|
||||
async saveAndQuit(contents) {
|
||||
return await ipcRenderer.invoke("buffer:saveAndQuit", contents)
|
||||
},
|
||||
|
52
package-lock.json
generated
52
package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "1.8.0",
|
||||
"license": "Commons Clause MIT",
|
||||
"dependencies": {
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"electron-log": "^5.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
"semver": "^7.6.3"
|
||||
@ -1517,6 +1518,57 @@
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/slugify": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz",
|
||||
"integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==",
|
||||
"dependencies": {
|
||||
"@sindresorhus/transliterate": "^1.0.0",
|
||||
"escape-string-regexp": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/slugify/node_modules/escape-string-regexp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/transliterate": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz",
|
||||
"integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==",
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/transliterate/node_modules/escape-string-regexp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@szmarczak/http-timer": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||
|
@ -78,6 +78,7 @@
|
||||
"vue-tsc": "^1.0.16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"electron-log": "^5.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
"semver": "^7.6.3"
|
||||
|
@ -2,9 +2,12 @@
|
||||
import { HeynoteEditor } from '../editor/editor.js'
|
||||
import { syntaxTree } from "@codemirror/language"
|
||||
import { toRaw } from 'vue';
|
||||
import { mapState } from 'pinia'
|
||||
import { mapState, mapWritableState, mapActions } from 'pinia'
|
||||
import { useErrorStore } from "../stores/error-store"
|
||||
import { useNotesStore } from "../stores/notes-store"
|
||||
|
||||
const NUM_EDITOR_INSTANCES = 5
|
||||
|
||||
export default {
|
||||
props: {
|
||||
theme: String,
|
||||
@ -41,8 +44,11 @@
|
||||
data() {
|
||||
return {
|
||||
syntaxTreeDebugContent: null,
|
||||
bufferFilePath: null,
|
||||
editor: null,
|
||||
editorCache: {
|
||||
lru: [],
|
||||
cache: {}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@ -130,34 +136,67 @@
|
||||
...mapState(useNotesStore, [
|
||||
"currentNotePath",
|
||||
]),
|
||||
...mapWritableState(useNotesStore, [
|
||||
"currentEditor",
|
||||
"currentNoteName",
|
||||
]),
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions(useErrorStore, ["addError"]),
|
||||
|
||||
loadBuffer(path) {
|
||||
if (this.editor) {
|
||||
this.editor.destroy()
|
||||
this.editor.hide()
|
||||
}
|
||||
// load buffer content and create editor
|
||||
this.bufferFilePath = path
|
||||
try {
|
||||
this.editor = new HeynoteEditor({
|
||||
element: this.$refs.editor,
|
||||
path: this.bufferFilePath,
|
||||
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,
|
||||
})
|
||||
|
||||
if (this.editorCache.cache[path]) {
|
||||
// editor is already loaded, just switch to it
|
||||
console.log("Switching to cached editor", path)
|
||||
toRaw(this.editor).hide()
|
||||
this.editor = this.editorCache.cache[path]
|
||||
toRaw(this.editor).show()
|
||||
//toRaw(this.editor).currenciesLoaded()
|
||||
this.currentEditor = toRaw(this.editor)
|
||||
window._heynote_editor = toRaw(this.editor)
|
||||
} catch (e) {
|
||||
alert("Error! " + e.message)
|
||||
throw e
|
||||
// move to end of LRU
|
||||
this.editorCache.lru = this.editorCache.lru.filter(p => p !== path)
|
||||
this.editorCache.lru.push(path)
|
||||
} else {
|
||||
// check if we need to free up a slot
|
||||
if (this.editorCache.lru.length >= NUM_EDITOR_INSTANCES) {
|
||||
const pathToFree = this.editorCache.lru.shift()
|
||||
console.log("Freeing up editor slot", pathToFree)
|
||||
this.editorCache.cache[pathToFree].destroy()
|
||||
delete this.editorCache.cache[pathToFree]
|
||||
this.editorCache.lru = this.editorCache.lru.filter(p => p !== pathToFree)
|
||||
}
|
||||
|
||||
// create new Editor instance
|
||||
console.log("Loading new editor", path)
|
||||
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,
|
||||
})
|
||||
this.currentEditor = toRaw(this.editor)
|
||||
window._heynote_editor = toRaw(this.editor)
|
||||
this.editorCache.cache[path] = this.editor
|
||||
this.editorCache.lru.push(path)
|
||||
} catch (e) {
|
||||
this.addError("Error! " + e.message)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
<script>
|
||||
import slugify from '@sindresorhus/slugify';
|
||||
|
||||
import { mapState, mapActions } from 'pinia'
|
||||
import { useNotesStore } from "../stores/notes-store"
|
||||
|
||||
@ -12,6 +14,9 @@
|
||||
tags: [],
|
||||
directoryTree: null,
|
||||
parentPath: "",
|
||||
errors: {
|
||||
name: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@ -64,17 +69,27 @@
|
||||
currentNoteDirectory() {
|
||||
return this.currentNotePath.split("/").slice(0, -1).join("/")
|
||||
},
|
||||
|
||||
nameInputClass() {
|
||||
return {
|
||||
"name-input": true,
|
||||
"error": this.errors.name,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions(useNotesStore, [
|
||||
"updateNotes",
|
||||
"createNewNoteFromActiveBlock",
|
||||
]),
|
||||
|
||||
onKeydown(event) {
|
||||
if (event.key === "Escape") {
|
||||
this.$emit("close")
|
||||
event.preventDefault()
|
||||
} if (event.key === "Enter") {
|
||||
this.submit()
|
||||
}
|
||||
},
|
||||
|
||||
@ -87,9 +102,29 @@
|
||||
}
|
||||
},
|
||||
|
||||
onSubmit(event) {
|
||||
event.preventDefault()
|
||||
console.log("Creating note", this.name)
|
||||
submit() {
|
||||
let slug = slugify(this.name)
|
||||
if (slug === "") {
|
||||
this.errors.name = true
|
||||
return
|
||||
}
|
||||
const parentPathPrefix = this.parentPath === "" ? "" : this.parentPath + "/"
|
||||
let path;
|
||||
for (let i=0; i<1000; i++) {
|
||||
let filename = slug + ".txt"
|
||||
path = parentPathPrefix + filename
|
||||
if (!this.notes[path]) {
|
||||
break
|
||||
}
|
||||
slug = slugify(this.name + "-" + i)
|
||||
}
|
||||
if (this.notes[path]) {
|
||||
console.error("Failed to create note, path already exists", path)
|
||||
this.errors.name = true
|
||||
return
|
||||
}
|
||||
console.log("Creating note", path)
|
||||
this.createNewNoteFromActiveBlock(path, this.name)
|
||||
this.$emit("close")
|
||||
//this.$emit("create", this.$refs.input.value)
|
||||
},
|
||||
@ -99,16 +134,17 @@
|
||||
|
||||
<template>
|
||||
<div class="fader" @keydown="onKeydown" tabindex="-1">
|
||||
<form class="new-note" tabindex="-1" @focusout="onFocusOut" ref="container" @submit="onSubmit">
|
||||
<form class="new-note" tabindex="-1" @focusout="onFocusOut" ref="container" @submit.prevent="submit">
|
||||
<div class="container">
|
||||
<h1>New Note from Block</h1>
|
||||
<input
|
||||
placeholder="Name"
|
||||
type="text"
|
||||
v-model="name"
|
||||
class="name-input"
|
||||
:class="nameInputClass"
|
||||
ref="nameInput"
|
||||
@keydown="onInputKeydown"
|
||||
@input="errors.name = false"
|
||||
/>
|
||||
|
||||
<label for="folder-select">Create in</label>
|
||||
@ -189,6 +225,8 @@
|
||||
outline: none
|
||||
border: 1px solid #fff
|
||||
outline: 2px solid #48b57e
|
||||
&.error
|
||||
background: #ffe9e9
|
||||
+dark-mode
|
||||
background: #3b3b3b
|
||||
color: rgba(255,255,255, 0.9)
|
||||
@ -219,4 +257,3 @@
|
||||
outline-color: #48b57e
|
||||
|
||||
</style>
|
||||
./folder-selector/FolderSelector.vue
|
@ -19,6 +19,8 @@
|
||||
return {
|
||||
"path": path,
|
||||
"name": metadata?.name || path,
|
||||
"folder": path.split("/").slice(0, -1).join("/"),
|
||||
"scratch": path === "buffer-dev.txt",
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -82,6 +84,13 @@
|
||||
this.$emit("close")
|
||||
}
|
||||
},
|
||||
|
||||
getItemClass(item, idx) {
|
||||
return {
|
||||
"selected": idx === this.selected,
|
||||
"scratch": item.scratch,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -100,12 +109,12 @@
|
||||
<li
|
||||
v-for="item, idx in filteredItems"
|
||||
:key="item.path"
|
||||
:class="idx === selected ? 'selected' : ''"
|
||||
:class="getItemClass(item, idx)"
|
||||
@click="selectItem(item.path)"
|
||||
ref="item"
|
||||
>
|
||||
<span class="name">{{ item.name }}</span>
|
||||
<span class="path">{{ item.path }}</span>
|
||||
<span class="path">{{ item.folder }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
@ -128,6 +137,7 @@
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 50%
|
||||
width: 420px
|
||||
transform: translateX(-50%)
|
||||
max-height: 100%
|
||||
box-sizing: border-box
|
||||
@ -176,6 +186,8 @@
|
||||
&.selected
|
||||
background: #48b57e
|
||||
color: #fff
|
||||
&.scratch
|
||||
font-weight: 600
|
||||
+dark-mode
|
||||
color: rgba(255,255,255, 0.53)
|
||||
&:hover
|
||||
@ -185,7 +197,15 @@
|
||||
color: rgba(255,255,255, 0.87)
|
||||
.name
|
||||
margin-right: 12px
|
||||
flex-shrink: 0
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
text-wrap: nowrap
|
||||
.path
|
||||
opacity: 0.6
|
||||
font-size: 12px
|
||||
flex-shrink: 1
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
text-wrap: nowrap
|
||||
</style>
|
||||
|
@ -5,3 +5,5 @@ export const LANGUAGE_CHANGE = "heynote-change"
|
||||
export const CURRENCIES_LOADED = "heynote-currencies-loaded"
|
||||
export const SET_CONTENT = "heynote-set-content"
|
||||
export const ADD_NEW_BLOCK = "heynote-add-new-block"
|
||||
export const DELETE_BLOCK = "heynote-delete-block"
|
||||
export const CURSOR_CHANGE = "heynote-cursor-change"
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { ViewPlugin, EditorView, Decoration, WidgetType, lineNumbers } from "@codemirror/view"
|
||||
import { layer, RectangleMarker } from "@codemirror/view"
|
||||
import { EditorState, RangeSetBuilder, StateField, Facet , StateEffect, RangeSet} from "@codemirror/state";
|
||||
import { EditorState, RangeSetBuilder, StateField, Facet , StateEffect, RangeSet, Transaction} from "@codemirror/state";
|
||||
import { syntaxTree, ensureSyntaxTree, syntaxTreeAvailable } from "@codemirror/language"
|
||||
import { Note, Document, NoteDelimiter } from "../lang-heynote/parser.terms.js"
|
||||
import { IterMode } from "@lezer/common";
|
||||
import { useNotesStore } from "../../stores/notes-store.js"
|
||||
import { heynoteEvent, LANGUAGE_CHANGE } from "../annotation.js";
|
||||
import { heynoteEvent, LANGUAGE_CHANGE, CURSOR_CHANGE } from "../annotation.js";
|
||||
import { mathBlock } from "./math.js"
|
||||
import { emptyBlockSelected } from "./select-all.js";
|
||||
|
||||
@ -404,6 +404,15 @@ function getSelectionSize(state, sel) {
|
||||
return count
|
||||
}
|
||||
|
||||
export function triggerCursorChange({state, dispatch}) {
|
||||
// Trigger empty change transaction that is annotated with CURRENCIES_LOADED
|
||||
// This will make Math blocks re-render so that currency conversions are applied
|
||||
dispatch(state.update({
|
||||
changes:{from: 0, to: 0, insert:""},
|
||||
annotations: [heynoteEvent.of(CURSOR_CHANGE), Transaction.addToHistory.of(false)],
|
||||
}))
|
||||
}
|
||||
|
||||
const emitCursorChange = (editor) => {
|
||||
const notesStore = useNotesStore()
|
||||
return ViewPlugin.fromClass(
|
||||
@ -411,8 +420,8 @@ const emitCursorChange = (editor) => {
|
||||
update(update) {
|
||||
// if the selection changed or the language changed (can happen without selection change),
|
||||
// emit a selection change event
|
||||
const langChange = update.transactions.some(tr => tr.annotations.some(a => a.value == LANGUAGE_CHANGE))
|
||||
if (update.selectionSet || langChange) {
|
||||
const shouldUpdate = update.transactions.some(tr => tr.annotations.some(a => a.value == LANGUAGE_CHANGE || a.value == CURSOR_CHANGE))
|
||||
if (update.selectionSet || shouldUpdate) {
|
||||
const cursorLine = getBlockLineFromPos(update.state, update.state.selection.main.head)
|
||||
|
||||
const selectionSize = update.state.selection.ranges.map(
|
||||
@ -425,6 +434,7 @@ const emitCursorChange = (editor) => {
|
||||
notesStore.currentSelectionSize = selectionSize
|
||||
notesStore.currentLanguage = block.language.name
|
||||
notesStore.currentLanguageAuto = block.language.auto
|
||||
notesStore.currentNoteName = editor.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { EditorSelection } from "@codemirror/state"
|
||||
import { heynoteEvent, LANGUAGE_CHANGE, CURRENCIES_LOADED, ADD_NEW_BLOCK } from "../annotation.js";
|
||||
import { EditorSelection, Transaction } from "@codemirror/state"
|
||||
import { heynoteEvent, LANGUAGE_CHANGE, CURRENCIES_LOADED, ADD_NEW_BLOCK, DELETE_BLOCK } from "../annotation.js";
|
||||
import { blockState, getActiveNoteBlock, getFirstNoteBlock, getLastNoteBlock, getNoteBlockFromPos } from "./block"
|
||||
import { moveLineDown, moveLineUp } from "./move-lines.js";
|
||||
import { selectAll } from "./select-all.js";
|
||||
@ -7,7 +7,7 @@ import { selectAll } from "./select-all.js";
|
||||
export { moveLineDown, moveLineUp, selectAll }
|
||||
|
||||
|
||||
function getBlockDelimiter(defaultToken, autoDetect) {
|
||||
export function getBlockDelimiter(defaultToken, autoDetect) {
|
||||
return `\n∞∞∞${autoDetect ? defaultToken + '-a' : defaultToken}\n`
|
||||
}
|
||||
|
||||
@ -317,6 +317,24 @@ export function triggerCurrenciesLoaded(state, dispatch) {
|
||||
// This will make Math blocks re-render so that currency conversions are applied
|
||||
dispatch(state.update({
|
||||
changes:{from: 0, to: 0, insert:""},
|
||||
annotations: [heynoteEvent.of(CURRENCIES_LOADED)],
|
||||
annotations: [heynoteEvent.of(CURRENCIES_LOADED), Transaction.addToHistory.of(false)],
|
||||
}))
|
||||
}
|
||||
|
||||
export const deleteBlock = (editor) => ({state, dispatch}) => {
|
||||
const block = getActiveNoteBlock(state)
|
||||
const blocks = state.facet(blockState)
|
||||
let replace = ""
|
||||
if (blocks.length == 1) {
|
||||
replace = getBlockDelimiter(editor.defaultBlockToken, editor.defaultBlockAutoDetect)
|
||||
}
|
||||
dispatch(state.update({
|
||||
changes: {
|
||||
from: block.range.from,
|
||||
to: block.range.to,
|
||||
insert: replace,
|
||||
},
|
||||
selection: EditorSelection.cursor(block.delimiter.from),
|
||||
annotations: [heynoteEvent.of(DELETE_BLOCK)],
|
||||
}))
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import { heynoteBase } from "./theme/base.js"
|
||||
import { getFontTheme } from "./theme/font-theme.js";
|
||||
import { customSetup } from "./setup.js"
|
||||
import { heynoteLang } from "./lang-heynote/heynote.js"
|
||||
import { noteBlockExtension, blockLineNumbers, blockState } from "./block/block.js"
|
||||
import { heynoteEvent, SET_CONTENT } from "./annotation.js";
|
||||
import { changeCurrentBlockLanguage, triggerCurrenciesLoaded } from "./block/commands.js"
|
||||
import { noteBlockExtension, blockLineNumbers, blockState, getActiveNoteBlock, triggerCursorChange } from "./block/block.js"
|
||||
import { heynoteEvent, SET_CONTENT, DELETE_BLOCK } from "./annotation.js";
|
||||
import { changeCurrentBlockLanguage, triggerCurrenciesLoaded, getBlockDelimiter, deleteBlock } from "./block/commands.js"
|
||||
import { formatBlockContent } from "./block/format-code.js"
|
||||
import { heynoteKeymap } from "./keymap.js"
|
||||
import { emacsKeymap } from "./emacs.js"
|
||||
@ -66,6 +66,7 @@ export class HeynoteEditor {
|
||||
this.setDefaultBlockLanguage(defaultBlockToken, defaultBlockAutoDetect)
|
||||
this.contentLoaded = false
|
||||
this.notesStore = useNotesStore()
|
||||
this.name = ""
|
||||
|
||||
|
||||
const state = EditorState.create({
|
||||
@ -179,7 +180,8 @@ export class HeynoteEditor {
|
||||
this.setReadOnly(true)
|
||||
throw new Error(`Failed to load note: ${e.message}`)
|
||||
}
|
||||
this.notesStore.currentNoteName = this.note.metadata?.name || this.path
|
||||
this.name = this.note.metadata?.name || this.path
|
||||
|
||||
return new Promise((resolve) => {
|
||||
// set buffer content
|
||||
this.view.dispatch({
|
||||
@ -262,7 +264,24 @@ export class HeynoteEditor {
|
||||
}
|
||||
|
||||
openCreateNote() {
|
||||
this.notesStore.openCreateNote()
|
||||
this.notesStore.openCreateNote(this)
|
||||
}
|
||||
|
||||
async createNewNoteFromActiveBlock(path, name) {
|
||||
const block = getActiveNoteBlock(this.view.state)
|
||||
if (!block) {
|
||||
return
|
||||
}
|
||||
const data = this.view.state.sliceDoc(block.range.from, block.range.to)
|
||||
await this.notesStore.saveNewNote(path, name, data)
|
||||
deleteBlock(this)(this.view)
|
||||
|
||||
// by using requestAnimationFrame we avoid a race condition where rendering the block backgrounds
|
||||
// would fail if we immediately opened the new note (since the block UI wouldn't have time to update
|
||||
// after the block was deleted)
|
||||
requestAnimationFrame(() => {
|
||||
this.notesStore.openNote(path)
|
||||
})
|
||||
}
|
||||
|
||||
setCurrentLanguage(lang, auto=false) {
|
||||
@ -311,6 +330,16 @@ export class HeynoteEditor {
|
||||
this.view.destroy()
|
||||
window.heynote.buffer.close(this.path)
|
||||
}
|
||||
|
||||
hide() {
|
||||
console.log("hiding element", this.view.dom)
|
||||
this.view.dom.style.setProperty("display", "none", "important")
|
||||
}
|
||||
show() {
|
||||
console.log("showing element", this.view.dom)
|
||||
this.view.dom.style.setProperty("display", "")
|
||||
triggerCursorChange(this.view)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
gotoPreviousParagraph, gotoNextParagraph,
|
||||
selectNextParagraph, selectPreviousParagraph,
|
||||
newCursorBelow, newCursorAbove,
|
||||
deleteBlock,
|
||||
} from "./block/commands.js"
|
||||
import { pasteCommand, copyCommand, cutCommand } from "./copy-paste.js"
|
||||
|
||||
@ -59,6 +60,7 @@ export function heynoteKeymap(editor) {
|
||||
["Mod-l", () => editor.openLanguageSelector()],
|
||||
["Mod-p", () => editor.openNoteSelector()],
|
||||
["Mod-s", () => editor.openCreateNote()],
|
||||
["Mod-Shift-d", deleteBlock(editor)],
|
||||
["Alt-Shift-f", formatBlockContent],
|
||||
["Mod-Alt-ArrowDown", newCursorBelow],
|
||||
["Mod-Alt-ArrowUp", newCursorAbove],
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { toRaw } from 'vue';
|
||||
import { defineStore } from "pinia"
|
||||
import { NoteFormat } from "../editor/note-format"
|
||||
|
||||
export const useNotesStore = defineStore("notes", {
|
||||
state: () => ({
|
||||
notes: {},
|
||||
currentEditor: null,
|
||||
currentNotePath: window.heynote.isDev ? "buffer-dev.txt" : "buffer.txt",
|
||||
currentNoteName: null,
|
||||
currentLanguage: null,
|
||||
@ -24,11 +27,6 @@ export const useNotesStore = defineStore("notes", {
|
||||
this.notes = notes
|
||||
},
|
||||
|
||||
createNewNote(path, content) {
|
||||
//window.heynote.buffer.save(path, content)
|
||||
this.updateNotes()
|
||||
},
|
||||
|
||||
openNote(path) {
|
||||
this.showNoteSelector = false
|
||||
this.showLanguageSelector = false
|
||||
@ -56,6 +54,26 @@ export const useNotesStore = defineStore("notes", {
|
||||
this.showNoteSelector = false
|
||||
this.showLanguageSelector = false
|
||||
},
|
||||
|
||||
async createNewNoteFromActiveBlock(path, name) {
|
||||
await toRaw(this.currentEditor).createNewNoteFromActiveBlock(path, name)
|
||||
},
|
||||
|
||||
async saveNewNote(path, name, content) {
|
||||
//window.heynote.buffer.save(path, content)
|
||||
//this.updateNotes()
|
||||
|
||||
if (this.notes[path]) {
|
||||
throw new Error(`Note already exists: ${path}`)
|
||||
}
|
||||
|
||||
const note = new NoteFormat()
|
||||
note.content = content
|
||||
note.metadata.name = name
|
||||
console.log("saving", path, note.serialize())
|
||||
await window.heynote.buffer.create(path, note.serialize())
|
||||
this.updateNotes()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Exception } from "sass";
|
||||
import { SETTINGS_CHANGE_EVENT, OPEN_SETTINGS_EVENT } from "../electron/constants";
|
||||
|
||||
const mediaMatch = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
@ -90,11 +91,14 @@ const Heynote = {
|
||||
localStorage.setItem(path, content)
|
||||
},
|
||||
|
||||
async create(path, content) {
|
||||
throw Exception("Not implemented")
|
||||
},
|
||||
|
||||
async saveAndQuit(contents) {
|
||||
|
||||
},
|
||||
|
||||
|
||||
async exists(path) {
|
||||
return true
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user