1
0
mirror of https://github.com/heyman/heynote.git synced 2025-03-08 20:11:38 +01:00
heynote/webapp/bridge.js
Jonatan Heyman 3107cb5368 Add right click context menu and improve application menu
- Add right-click context menu with undo/redo/cut/copy/paste/select all options, as well as Delete block and Move block to another buffer
- Add Delete Block and Move Block to another buffer in the application menu
- Change so that we use our custom "undo" event instead of relying on the default undo event, since there were cases when the default undo didn't trigger the CodeMirror undo reliably
2025-01-09 17:19:59 +01:00

289 lines
7.9 KiB
JavaScript

import { Exception } from "sass";
import { SETTINGS_CHANGE_EVENT, OPEN_SETTINGS_EVENT } from "@/src/common/constants";
import { NoteFormat } from "../src/common/note-format";
const NOTE_KEY_PREFIX = "heynote-library__"
const mediaMatch = window.matchMedia('(prefers-color-scheme: dark)')
let themeCallback = null
mediaMatch.addEventListener("change", async (event) => {
if (themeCallback) {
themeCallback((await Heynote.themeMode.get()).computed)
}
})
const isMobileDevice = window.matchMedia("(max-width: 600px)").matches
let autoUpdateCallbacks = null
let currencyData = null
let platform
// In the latest version of Playwright, the window.navigator.userAgentData.platform is not reported correctly on Mac,
// wo we'll fallback to deprecated window.navigator.platform which still works
if (__TESTS__ && window.navigator.platform.indexOf("Mac") !== -1) {
platform = {
isMac: true,
isWindows: false,
isLinux: false,
}
} else {
const uaPlatform = window.navigator?.userAgentData?.platform || window.navigator.platform
if (uaPlatform.indexOf("Win") !== -1) {
platform = {
isMac: false,
isWindows: true,
isLinux: false,
}
} else if (uaPlatform.indexOf("Linux") !== -1) {
platform = {
isMac: false,
isWindows: false,
isLinux: true,
}
} else {
platform = {
isMac: true,
isWindows: false,
isLinux: false,
}
}
}
platform.isWebApp = true
class IpcRenderer {
constructor() {
this.callbacks = {}
}
on(event, callback) {
if (!this.callbacks[event]) {
this.callbacks[event] = []
}
this.callbacks[event].push(callback)
}
off(event, callback) {
if (this.callbacks[event]) {
this.callbacks[event] = this.callbacks[event].filter(cb => cb !== callback)
}
}
send(event, ...args) {
if (this.callbacks[event]) {
for (const callback of this.callbacks[event]) {
callback(null, ...args)
}
}
}
}
const ipcRenderer = new IpcRenderer()
// get initial settings
let settingsData = localStorage.getItem("settings")
let initialSettings = {
keymap: "default",
emacsMetaKey: "alt",
showLineNumberGutter: true,
showFoldGutter: true,
bracketClosing: false,
}
if (settingsData !== null) {
initialSettings = Object.assign(initialSettings, JSON.parse(settingsData))
}
function noteKey(path) {
return NOTE_KEY_PREFIX + path
}
function getNoteMetadata(content) {
const firstSeparator = content.indexOf("\n∞∞∞")
if (firstSeparator === -1) {
return null
}
try {
const metadata = JSON.parse(content.slice(0, firstSeparator).trim())
return {"name": metadata.name}
} catch (e) {
return {}
}
}
// Migrate single buffer (Heynote pre 2.0) in localStorage to notes library
// At some point we can remove this migration code
function migrateBufferFileToLibrary() {
if (!("buffer" in localStorage)) {
// nothing to migrate
return
}
if (Object.keys(localStorage).filter(key => key.startsWith(NOTE_KEY_PREFIX)).length > 0) {
// already migrated
return
}
console.log("Migrating single buffer to notes library")
let content = localStorage.getItem("buffer")
const metadata = getNoteMetadata(content)
if (!metadata || !metadata.name) {
console.log("Adding metadata to Scratch note")
const note = NoteFormat.load(content)
note.metadata.name = "Scratch"
content = note.serialize()
}
localStorage.setItem("heynote-library__scratch.txt", content)
localStorage.removeItem("buffer")
}
migrateBufferFileToLibrary()
const Heynote = {
platform: platform,
defaultFontFamily: "Hack",
defaultFontSize: isMobileDevice ? 16 : 12,
buffer: {
async load(path) {
//console.log("loading", path)
const content = localStorage.getItem(noteKey(path))
return content === null ? '{"formatVersion":"1.0.0","name":"Scratch"}\n∞∞∞text-a\n' : content
},
async save(path, content) {
//console.log("saving", path, content)
localStorage.setItem(noteKey(path), content)
},
async create(path, content) {
localStorage.setItem(noteKey(path), content)
},
async delete(path) {
localStorage.removeItem(noteKey(path))
},
async move(path, newPath) {
const content = localStorage.getItem(noteKey(path))
localStorage.setItem(noteKey(newPath), content)
localStorage.removeItem(noteKey(path))
},
async saveAndQuit(contents) {
},
async exists(path) {
return localStorage.getItem(noteKey(path)) !== null
},
async getList() {
//return {"scratch.txt": {name:"Scratch"}}
const notes = {}
for (let [key, content] of Object.entries(localStorage)) {
if (key.startsWith(NOTE_KEY_PREFIX)) {
const path = key.slice(NOTE_KEY_PREFIX.length)
notes[path] = getNoteMetadata(content)
}
}
return notes
},
async getDirectoryList() {
const directories = new Set()
for (let key in localStorage) {
if (key.startsWith(NOTE_KEY_PREFIX)) {
const path = key.slice(NOTE_KEY_PREFIX.length)
const parts = path.split("/")
if (parts.length > 1) {
for (let i = 1; i < parts.length; i++) {
directories.add(parts.slice(0, i).join("/"))
}
}
}
}
//console.log("directories", directories)
return [...directories]
},
async close(path) {
},
_onChangeCallbacks: {},
addOnChangeCallback(path, callback) {
},
removeOnChangeCallback(path, callback) {
},
},
mainProcess: {
on(event, callback) {
ipcRenderer.on(event, callback)
},
off(event, callback) {
ipcRenderer.off(event, callback)
},
invoke(event, ...args) {
}
},
settings: initialSettings,
setSettings(settings) {
localStorage.setItem("settings", JSON.stringify(settings))
ipcRenderer.send(SETTINGS_CHANGE_EVENT, settings)
},
themeMode: {
set: (mode) => {
localStorage.setItem("theme", mode)
themeCallback(mode)
//console.log("set theme to", mode)
},
get: async () => {
const theme = localStorage.getItem("theme") || "system"
const systemTheme = mediaMatch.matches ? "dark" : "light"
return {
theme: theme,
computed: theme === "system" ? systemTheme : theme,
}
},
onChange: (callback) => {
themeCallback = callback
},
removeListener() {
themeCallback = null
},
initial: localStorage.getItem("theme") || "system",
},
getCurrencyData: async () => {
if (currencyData !== null) {
return currencyData
}
const response = await fetch("https://currencies.heynote.com/rates.json", {cache: "no-cache"})
currencyData = JSON.parse(await response.text())
return currencyData
},
async getVersion() {
return __APP_VERSION__ + " (" + __GIT_HASH__ + ")"
},
async getInitErrors() {
},
setWindowTitle(title) {
document.title = title + " - Heynote"
},
}
export { Heynote, ipcRenderer}