mirror of
https://github.com/marktext/marktext.git
synced 2025-05-02 17:50:02 +08:00
update or notice file changed on disk (#796)
* update or notice file changed on disk * update changelog * fix some typo and optimize some codes
This commit is contained in:
parent
499a3cd36b
commit
a41f751f2f
1
.github/CHANGELOG.md
vendored
1
.github/CHANGELOG.md
vendored
@ -20,6 +20,7 @@ This update **fixes a XSS security vulnerability** when exporting a document.
|
||||
- Clicking a link should open it in the browser (#425)
|
||||
- Support maxOS `dark mode`, when you change `mode dark or light` in system, Mark Text will change its theme.
|
||||
- Add two new themes Ulysses Light and Graphite Light theme.
|
||||
- Watch file changed in tabs and show a notice(autoSave is `false`) or update the file(autoSave is `true`)
|
||||
|
||||
**:butterfly:Optimization**
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { promisify } from 'util'
|
||||
import { BrowserWindow, dialog, ipcMain, shell } from 'electron'
|
||||
import appWindow from '../window'
|
||||
import { EXTENSION_HASN, EXTENSIONS, PANDOC_EXTENSIONS, URL_REG } from '../config'
|
||||
import { loadMarkdownFile, writeFile, writeMarkdownFile } from '../utils/filesystem'
|
||||
import { writeFile, writeMarkdownFile } from '../utils/filesystem'
|
||||
import appMenu from '../menu'
|
||||
import { getPath, isMarkdownFile, isMarkdownFileOrLink, normalizeAndResolvePath, log, isFile, isDirectory, getRecommendTitle } from '../utils'
|
||||
import userPreference from '../preference'
|
||||
@ -80,6 +80,10 @@ const handleResponseForSave = (e, { id, markdown, pathname, options }) => {
|
||||
|
||||
return writeMarkdownFile(pathname, markdown, options, win)
|
||||
.then(() => {
|
||||
if (!alreadyExistOnDisk) {
|
||||
// it's a new created file, need watch
|
||||
appWindow.watcher.watch(win, pathname, 'file')
|
||||
}
|
||||
const filename = path.basename(pathname)
|
||||
win.webContents.send('AGANI::set-pathname', { id, pathname, filename })
|
||||
return id
|
||||
@ -175,6 +179,12 @@ ipcMain.on('AGANI::response-file-save-as', (e, { id, markdown, pathname, options
|
||||
if (filePath) {
|
||||
writeMarkdownFile(filePath, markdown, options, win)
|
||||
.then(() => {
|
||||
// need watch file after `save as`
|
||||
if (pathname !== filePath) {
|
||||
appWindow.watcher.watch(win, filePath, 'file')
|
||||
// unWatch the old file.
|
||||
appWindow.watcher.unWatch(win, pathname, 'file')
|
||||
}
|
||||
const filename = path.basename(filePath)
|
||||
win.webContents.send('AGANI::set-pathname', { id, pathname: filePath, filename })
|
||||
})
|
||||
@ -333,13 +343,7 @@ export const openFileOrFolder = (win, pathname) => {
|
||||
if (openFilesInNewWindow) {
|
||||
appWindow.createWindow(resolvedPath)
|
||||
} else {
|
||||
loadMarkdownFile(resolvedPath).then(rawDocument => {
|
||||
newTab(win, rawDocument)
|
||||
}).catch(err => {
|
||||
// TODO: Handle error --> create a end-user error handler.
|
||||
console.error('[ERROR] Cannot open file or directory.')
|
||||
log(err)
|
||||
})
|
||||
appWindow.newTab(win, pathname)
|
||||
}
|
||||
} else if (isDirectory(resolvedPath)) {
|
||||
appWindow.createWindow(resolvedPath)
|
||||
@ -400,7 +404,7 @@ export const autoSave = (menuItem, browserWindow) => {
|
||||
const { checked } = menuItem
|
||||
userPreference.setItem('autoSave', checked)
|
||||
.then(() => {
|
||||
for (const win of appWindow.windows.values()) {
|
||||
for (const { win } of appWindow.windows.values()) {
|
||||
win.webContents.send('AGANI::user-preference', { autoSave: checked })
|
||||
}
|
||||
})
|
||||
|
@ -155,7 +155,7 @@ ipcMain.on('AGANI::set-user-preference', (e, pre) => {
|
||||
Object.keys(pre).map(key => {
|
||||
preference.setItem(key, pre[key])
|
||||
.then(() => {
|
||||
for (const win of appWindow.windows.values()) {
|
||||
for (const { win } of appWindow.windows.values()) {
|
||||
win.webContents.send('AGANI::user-preference', { [key]: pre[key] })
|
||||
}
|
||||
})
|
||||
|
@ -5,6 +5,11 @@ import chokidar from 'chokidar'
|
||||
import { getUniqueId, log, hasMarkdownExtension } from './utils'
|
||||
import { loadMarkdownFile } from './utils/filesystem'
|
||||
|
||||
const EVENT_NAME = {
|
||||
dir: 'AGANI::update-object-tree',
|
||||
file: 'AGANI::update-file'
|
||||
}
|
||||
|
||||
const add = async (win, pathname) => {
|
||||
const stats = await promisify(fs.stat)(pathname)
|
||||
const birthTime = stats.birthtime
|
||||
@ -28,15 +33,15 @@ const add = async (win, pathname) => {
|
||||
})
|
||||
}
|
||||
|
||||
const unlink = (win, pathname) => {
|
||||
const unlink = (win, pathname, type) => {
|
||||
const file = { pathname }
|
||||
win.webContents.send('AGANI::update-object-tree', {
|
||||
win.webContents.send(EVENT_NAME[type], {
|
||||
type: 'unlink',
|
||||
change: file
|
||||
})
|
||||
}
|
||||
|
||||
const change = async (win, pathname) => {
|
||||
const change = async (win, pathname, type) => {
|
||||
const isMarkdown = hasMarkdownExtension(pathname)
|
||||
|
||||
if (isMarkdown) {
|
||||
@ -45,7 +50,7 @@ const change = async (win, pathname) => {
|
||||
pathname,
|
||||
data
|
||||
}
|
||||
win.webContents.send('AGANI::update-object-tree', {
|
||||
win.webContents.send(EVENT_NAME[type], {
|
||||
type: 'change',
|
||||
change: file
|
||||
})
|
||||
@ -83,18 +88,18 @@ class Watcher {
|
||||
this.watchers = {}
|
||||
}
|
||||
// return a unwatch function
|
||||
watch (win, dir) {
|
||||
watch (win, watchPath, type = 'dir'/* file or dir */) {
|
||||
const id = getUniqueId()
|
||||
const watcher = chokidar.watch(dir, {
|
||||
const watcher = chokidar.watch(watchPath, {
|
||||
ignored: /node_modules|\.git/,
|
||||
ignoreInitial: false,
|
||||
ignoreInitial: type === 'file',
|
||||
persistent: true
|
||||
})
|
||||
|
||||
watcher
|
||||
.on('add', pathname => add(win, pathname))
|
||||
.on('change', pathname => change(win, pathname))
|
||||
.on('unlink', pathname => unlink(win, pathname))
|
||||
.on('change', pathname => change(win, pathname, type))
|
||||
.on('unlink', pathname => unlink(win, pathname, type))
|
||||
.on('addDir', pathname => addDir(win, pathname))
|
||||
.on('unlinkDir', pathname => unlinkDir(win, pathname))
|
||||
.on('error', error => {
|
||||
@ -103,7 +108,9 @@ class Watcher {
|
||||
|
||||
this.watchers[id] = {
|
||||
win,
|
||||
watcher
|
||||
watcher,
|
||||
pathname: watchPath,
|
||||
type
|
||||
}
|
||||
|
||||
// unwatcher function
|
||||
@ -115,6 +122,39 @@ class Watcher {
|
||||
}
|
||||
}
|
||||
|
||||
// unWatch some single watch
|
||||
unWatch (win, watchPath, type = 'dir') {
|
||||
for (const id of Object.keys(this.watchers)) {
|
||||
const w = this.watchers[id]
|
||||
if (
|
||||
w.win === win &&
|
||||
w.pathname === watchPath &&
|
||||
w.type === type
|
||||
) {
|
||||
w.watcher.close()
|
||||
delete this.watchers[id]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unwatch for one window, (remove all the watchers in one window)
|
||||
unWatchWin (win) {
|
||||
const watchers = []
|
||||
const watchIds = []
|
||||
for (const id of Object.keys(this.watchers)) {
|
||||
const w = this.watchers[id]
|
||||
if (w.win === win) {
|
||||
watchers.push(w.watcher)
|
||||
watchIds.push(id)
|
||||
}
|
||||
}
|
||||
if (watchers.length) {
|
||||
watchIds.forEach(id => delete this.watchers[id])
|
||||
watchers.forEach(watcher => watcher.close())
|
||||
}
|
||||
}
|
||||
|
||||
clear () {
|
||||
Object.keys(this.watchers).forEach(id => this.watchers[id].watcher.close())
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { app, BrowserWindow, screen } from 'electron'
|
||||
import { app, BrowserWindow, screen, ipcMain } from 'electron'
|
||||
import windowStateKeeper from 'electron-window-state'
|
||||
import { getOsLineEndingName, loadMarkdownFile, getDefaultTextDirection } from './utils/filesystem'
|
||||
import appMenu from './menu'
|
||||
@ -6,12 +6,31 @@ import Watcher from './watcher'
|
||||
import { isMarkdownFile, isDirectory, normalizeAndResolvePath, log } from './utils'
|
||||
import { TITLE_BAR_HEIGHT, defaultWinOptions, isLinux } from './config'
|
||||
import userPreference from './preference'
|
||||
import { newTab } from './actions/file'
|
||||
|
||||
class AppWindow {
|
||||
constructor () {
|
||||
this.focusedWindowId = -1
|
||||
this.windows = new Map()
|
||||
this.watcher = new Watcher()
|
||||
this.listen()
|
||||
}
|
||||
|
||||
listen () {
|
||||
// listen for file watch from renderer process eg
|
||||
// 1. click file in folder.
|
||||
// 2. new tab and save it.
|
||||
// 3. close tab(s) need unwatch.
|
||||
ipcMain.on('AGANI::file-watch', (e, { pathname, watch }) => {
|
||||
const win = BrowserWindow.fromWebContents(e.sender)
|
||||
if (watch) {
|
||||
// listen for file `change` and `unlink`
|
||||
this.watcher.watch(win, pathname, 'file')
|
||||
} else {
|
||||
// unlisten for file `change` and `unlink`
|
||||
this.watcher.unWatch(win, pathname, 'file')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ensureWindowPosition (mainWindowState) {
|
||||
@ -79,10 +98,7 @@ class AppWindow {
|
||||
}
|
||||
|
||||
const win = new BrowserWindow(winOpt)
|
||||
windows.set(win.id, {
|
||||
win,
|
||||
watchers: []
|
||||
})
|
||||
windows.set(win.id, { win })
|
||||
|
||||
// create a menu for the current window
|
||||
appMenu.addWindowMenuWithListener(win)
|
||||
@ -97,43 +113,11 @@ class AppWindow {
|
||||
// open single markdown file
|
||||
if (pathname && isMarkdownFile(pathname)) {
|
||||
appMenu.addRecentlyUsedDocument(pathname)
|
||||
loadMarkdownFile(pathname)
|
||||
.then(data => {
|
||||
const {
|
||||
markdown,
|
||||
filename,
|
||||
pathname,
|
||||
isUtf8BomEncoded,
|
||||
lineEnding,
|
||||
adjustLineEndingOnSave,
|
||||
isMixedLineEndings,
|
||||
textDirection
|
||||
} = data
|
||||
|
||||
appMenu.updateLineEndingnMenu(lineEnding)
|
||||
appMenu.updateTextDirectionMenu(textDirection)
|
||||
win.webContents.send('AGANI::open-single-file', {
|
||||
markdown,
|
||||
filename,
|
||||
pathname,
|
||||
options: {
|
||||
isUtf8BomEncoded,
|
||||
lineEnding,
|
||||
adjustLineEndingOnSave
|
||||
try {
|
||||
this.openFile(win, pathname)
|
||||
} catch (err) {
|
||||
log(err)
|
||||
}
|
||||
})
|
||||
|
||||
// Notify user about mixed endings
|
||||
if (isMixedLineEndings) {
|
||||
win.webContents.send('AGANI::show-notification', {
|
||||
title: 'Mixed Line Endings',
|
||||
type: 'error',
|
||||
message: `The document has mixed line endings which are automatically normalized to ${lineEnding.toUpperCase()}.`,
|
||||
time: 20000
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(log)
|
||||
// open directory / folder
|
||||
} else if (pathname && isDirectory(pathname)) {
|
||||
appMenu.addRecentlyUsedDocument(pathname)
|
||||
@ -176,6 +160,7 @@ class AppWindow {
|
||||
|
||||
// set renderer arguments
|
||||
const { codeFontFamily, codeFontSize, theme } = userPreference.getAll()
|
||||
// wow, this can be accessesed in renderer process.
|
||||
win.stylePrefs = {
|
||||
codeFontFamily,
|
||||
codeFontSize,
|
||||
@ -192,9 +177,57 @@ class AppWindow {
|
||||
return win
|
||||
}
|
||||
|
||||
openFile = async (win, filePath) => {
|
||||
const data = await loadMarkdownFile(filePath)
|
||||
const {
|
||||
markdown,
|
||||
filename,
|
||||
pathname,
|
||||
isUtf8BomEncoded,
|
||||
lineEnding,
|
||||
adjustLineEndingOnSave,
|
||||
isMixedLineEndings,
|
||||
textDirection
|
||||
} = data
|
||||
|
||||
appMenu.updateLineEndingnMenu(lineEnding)
|
||||
appMenu.updateTextDirectionMenu(textDirection)
|
||||
win.webContents.send('AGANI::open-single-file', {
|
||||
markdown,
|
||||
filename,
|
||||
pathname,
|
||||
options: {
|
||||
isUtf8BomEncoded,
|
||||
lineEnding,
|
||||
adjustLineEndingOnSave
|
||||
}
|
||||
})
|
||||
// listen for file `change` and `unlink`
|
||||
this.watcher.watch(win, filePath, 'file')
|
||||
// Notify user about mixed endings
|
||||
if (isMixedLineEndings) {
|
||||
win.webContents.send('AGANI::show-notification', {
|
||||
title: 'Mixed Line Endings',
|
||||
type: 'error',
|
||||
message: `The document has mixed line endings which are automatically normalized to ${lineEnding.toUpperCase()}.`,
|
||||
time: 20000
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
newTab (win, filePath) {
|
||||
this.watcher.watch(win, filePath, 'file')
|
||||
loadMarkdownFile(filePath).then(rawDocument => {
|
||||
newTab(win, rawDocument)
|
||||
}).catch(err => {
|
||||
// TODO: Handle error --> create a end-user error handler.
|
||||
console.error('[ERROR] Cannot open file or directory.')
|
||||
log(err)
|
||||
})
|
||||
}
|
||||
|
||||
openFolder (win, pathname) {
|
||||
const unwatcher = this.watcher.watch(win, pathname)
|
||||
this.windows.get(win.id).watchers.push(unwatcher)
|
||||
this.watcher.watch(win, pathname, 'dir')
|
||||
try {
|
||||
win.webContents.send('AGANI::open-project', pathname)
|
||||
} catch (err) {
|
||||
@ -206,10 +239,7 @@ class AppWindow {
|
||||
if (!win) return
|
||||
const { windows } = this
|
||||
if (windows.has(win.id)) {
|
||||
const { watchers } = windows.get(win.id)
|
||||
if (watchers && watchers.length) {
|
||||
watchers.forEach(w => w())
|
||||
}
|
||||
this.watcher.unWatchWin(win)
|
||||
windows.delete(win.id)
|
||||
}
|
||||
appMenu.removeWindowMenu(win.id)
|
||||
|
@ -145,6 +145,7 @@
|
||||
dispatch('LINTEN_FOR_PRINT_SERVICE_CLEARUP')
|
||||
dispatch('LINTEN_FOR_EXPORT_SUCCESS')
|
||||
dispatch('LISTEN_FOR_SET_TEXT_DIRECTION')
|
||||
dispatch('LISTEN_FOR_FILE_CHANGE')
|
||||
dispatch('LISTEN_FOR_TEXT_DIRECTION_MENU')
|
||||
// module: notification
|
||||
dispatch('LISTEN_FOR_NOTIFICATION')
|
||||
|
@ -29,6 +29,11 @@ export const fileMixins = {
|
||||
|
||||
const fileState = isOpened || getFileStateFromData(data)
|
||||
this.$store.dispatch('UPDATE_CURRENT_FILE', fileState)
|
||||
// ask main process to watch this file changes
|
||||
this.$store.dispatch('ASK_FILE_WATCH', {
|
||||
pathname,
|
||||
watch: true
|
||||
})
|
||||
|
||||
ipcRenderer.send("AGANI::add-recently-used-document", pathname)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import template from './index.html'
|
||||
import { getUniqueId } from '../../util'
|
||||
import './index.css'
|
||||
|
||||
const INON_HASH = {
|
||||
@ -8,8 +9,22 @@ const INON_HASH = {
|
||||
info: 'icon-info'
|
||||
}
|
||||
|
||||
const COLOR_HASH = {
|
||||
primary: 'var(--themeColor)',
|
||||
error: 'var(--deleteColor)',
|
||||
warning: 'var(--deleteColor)',
|
||||
info: '#999999'
|
||||
}
|
||||
|
||||
const notification = {
|
||||
name: 'notify',
|
||||
noticeCache: {},
|
||||
// it's a dirty implement of clear, because not remove all the event listeners. need refactor.
|
||||
clear () {
|
||||
Object.keys(this.noticeCache).forEach(key => {
|
||||
this.noticeCache[key].remove()
|
||||
})
|
||||
},
|
||||
notify ({
|
||||
time = 10000,
|
||||
title = '',
|
||||
@ -20,6 +35,7 @@ const notification = {
|
||||
let rs
|
||||
let rj
|
||||
let timer = null
|
||||
const id = getUniqueId()
|
||||
|
||||
const fragment = document.createElement('div')
|
||||
fragment.innerHTML = template
|
||||
@ -40,7 +56,7 @@ const notification = {
|
||||
target = noticeContainer.querySelector('.confirm')
|
||||
}
|
||||
|
||||
bgNotice.style.backgroundColor = `var(--${type})`
|
||||
bgNotice.style.backgroundColor = `${COLOR_HASH[type]}`
|
||||
|
||||
fluent.style.height = offsetHeight * 2 + 'px'
|
||||
fluent.style.width = offsetHeight * 2 + 'px'
|
||||
@ -117,9 +133,14 @@ const notification = {
|
||||
close.removeEventListener('click', closeHandler)
|
||||
noticeContainer.remove()
|
||||
rePositionNotices()
|
||||
if (this.noticeCache[id]) {
|
||||
delete this.noticeCache[id]
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
this.noticeCache[id] = { remove }
|
||||
|
||||
noticeContainer.addEventListener('mousemove', mousemoveHandler)
|
||||
noticeContainer.addEventListener('mouseleave', mouseleaveHandler)
|
||||
target.addEventListener('click', clickHandler)
|
||||
|
@ -50,6 +50,35 @@ const mutations = {
|
||||
}
|
||||
}
|
||||
},
|
||||
LOAD_CHANGE (state, change) {
|
||||
const { tabs, currentFile } = state
|
||||
const { data, pathname } = change
|
||||
const { isMixedLineEndings, lineEnding, adjustLineEndingOnSave, isUtf8BomEncoded, markdown, textDirection, filename } = data
|
||||
const options = { isUtf8BomEncoded, lineEnding, adjustLineEndingOnSave, textDirection }
|
||||
const fileState = getSingleFileState({ markdown, filename, pathname, options })
|
||||
if (isMixedLineEndings) {
|
||||
notice.notify({
|
||||
title: 'Line Ending',
|
||||
message: `${filename} has mixed line endings which are automatically normalized to ${lineEnding.toUpperCase()}.`,
|
||||
type: 'primary',
|
||||
time: 20000,
|
||||
showConfirm: false
|
||||
})
|
||||
}
|
||||
for (const tab of tabs) {
|
||||
if (tab.pathname === pathname) {
|
||||
Object.assign(tab, fileState)
|
||||
break
|
||||
}
|
||||
}
|
||||
state.tabs = tabs
|
||||
if (pathname === currentFile.pathname) {
|
||||
Object.assign(currentFile, fileState)
|
||||
state.currentFile = currentFile
|
||||
const { cursor, history } = currentFile
|
||||
bus.$emit('file-changed', { markdown, cursor, renderCursor: true, history })
|
||||
}
|
||||
},
|
||||
SET_PATHNAME (state, file) {
|
||||
const { filename, pathname, id } = file
|
||||
if (id === state.currentFile.id && pathname) {
|
||||
@ -112,6 +141,12 @@ const mutations = {
|
||||
CLOSE_TABS (state, arr) {
|
||||
arr.forEach(id => {
|
||||
const index = state.tabs.findIndex(f => f.id === id)
|
||||
const { pathname } = state.tabs.find(f => f.id === id)
|
||||
if (pathname) {
|
||||
// close tab and unwatch this file
|
||||
ipcRenderer.send('AGANI::file-watch', { pathname, watch: false })
|
||||
}
|
||||
|
||||
state.tabs.splice(index, 1)
|
||||
if (state.currentFile.id === id) {
|
||||
state.currentFile = {}
|
||||
@ -178,8 +213,11 @@ const actions = {
|
||||
})
|
||||
},
|
||||
|
||||
REMOVE_FILE_IN_TABS ({ commit }, file) {
|
||||
REMOVE_FILE_IN_TABS ({ commit, dispatch }, file) {
|
||||
commit('REMOVE_FILE_WITHIN_TABS', file)
|
||||
// unwatch this file
|
||||
const { pathname } = file
|
||||
dispatch('ASK_FILE_WATCH', { pathname, watch: false })
|
||||
},
|
||||
|
||||
// need update line ending when change between windows.
|
||||
@ -416,7 +454,7 @@ const actions = {
|
||||
|
||||
if (isMixedLineEndings) {
|
||||
const { filename, lineEnding } = markdownDocument
|
||||
notice({
|
||||
notice.notify({
|
||||
title: 'Line Ending',
|
||||
message: `${filename} has mixed line endings which are automatically normalized to ${lineEnding.toUpperCase()}.`,
|
||||
type: 'primary',
|
||||
@ -593,6 +631,43 @@ const actions = {
|
||||
commit('SET_TEXT_DIRECTION', textDirection)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_FILE_CHANGE ({ commit, state, rootState }) {
|
||||
ipcRenderer.on('AGANI::update-file', (e, { type, change }) => {
|
||||
if (type === 'unlink') {
|
||||
return notice.notify({
|
||||
title: 'File Removed on Disk',
|
||||
message: `${change.pathname} has been removed or moved to other place`,
|
||||
type: 'warning',
|
||||
time: 0,
|
||||
showConfirm: false
|
||||
})
|
||||
} else {
|
||||
const { autoSave } = rootState.preferences
|
||||
const { windowActive } = rootState
|
||||
const { filename } = change.data
|
||||
if (windowActive) return
|
||||
if (autoSave) {
|
||||
commit('LOAD_CHANGE', change)
|
||||
} else {
|
||||
notice.clear()
|
||||
notice.notify({
|
||||
title: 'File Changed on Disk',
|
||||
message: `${filename} has been changed on disk, do you want to reload it?`,
|
||||
showConfirm: true,
|
||||
time: 0
|
||||
})
|
||||
.then(() => {
|
||||
commit('LOAD_CHANGE', change)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
ASK_FILE_WATCH ({ commit }, { pathname, watch }) {
|
||||
ipcRenderer.send('AGANI::file-watch', { pathname, watch })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ export const getSingleFileState = ({ id = getUniqueId(), markdown, filename, pat
|
||||
// TODO(refactor:renderer/editor): Replace this function with `createDocumentState`.
|
||||
|
||||
const fileState = JSON.parse(JSON.stringify(defaultFileState))
|
||||
const { isUtf8BomEncoded, lineEnding, adjustLineEndingOnSave } = options
|
||||
const { isUtf8BomEncoded, lineEnding, adjustLineEndingOnSave, textDirection = 'ltr' } = options
|
||||
|
||||
assertLineEnding(adjustLineEndingOnSave, lineEnding)
|
||||
|
||||
@ -102,6 +102,7 @@ export const getSingleFileState = ({ id = getUniqueId(), markdown, filename, pat
|
||||
pathname,
|
||||
isUtf8BomEncoded,
|
||||
lineEnding,
|
||||
textDirection,
|
||||
adjustLineEndingOnSave
|
||||
})
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ const actions = {
|
||||
if (autoSave) {
|
||||
const { pathname, markdown } = state
|
||||
const options = getOptionsFromState(rootState.editor)
|
||||
if (autoSave && pathname) {
|
||||
if (pathname) {
|
||||
commit('SET_SAVE_STATUS', true)
|
||||
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown, options })
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user