mirror of
https://github.com/marktext/marktext.git
synced 2025-05-03 00:19:35 +08:00
parent
3e28b7e328
commit
39f5400544
13
.github/CHANGELOG.md
vendored
13
.github/CHANGELOG.md
vendored
@ -1,6 +1,15 @@
|
||||
### 0.7.18
|
||||
### 0.8.4
|
||||
|
||||
**Bug fix**
|
||||
**:cactus:Feature**
|
||||
|
||||
- Add user preferences in `Mark Text menu`, the shoutcut is `CmdorCtrl + ,`, you can set the default `theme` and `autoSave`.
|
||||
- Add `autoSave` to `file menu`, the default value is in `preferences.md` which you can open in `Mark Text menu`. #45
|
||||
|
||||
**:butterfly:Optimization**
|
||||
|
||||
- Theme can be saved in user preferences now #16
|
||||
|
||||
**:beetle:Bug fix**
|
||||
|
||||
- fix: prevent open image or file directly when drag and drop over Mark Text #42
|
||||
- fix: set theme to all the open window not just the active one.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "marktext",
|
||||
"version": "0.7.17",
|
||||
"version": "0.8.4",
|
||||
"author": "Jocs <luoran1988@126.com>",
|
||||
"description": "Next generation markdown editor",
|
||||
"license": "MIT",
|
||||
|
@ -6,6 +6,7 @@ import path from 'path'
|
||||
import { app, dialog, ipcMain, BrowserWindow } from 'electron'
|
||||
import createWindow, { windows } from '../createWindow'
|
||||
import { EXTENSIONS, EXTENSION_HASN } from '../config'
|
||||
import { getUserPreference, setUserPreference } from '../utils'
|
||||
|
||||
const watchAndReload = (pathname, win) => { // when i build, and failed.
|
||||
// const watcher = chokidar.watch(pathname, {
|
||||
@ -114,6 +115,13 @@ ipcMain.on('AGANI::response-close-confirm', (e, { filename, pathname, markdown }
|
||||
})
|
||||
|
||||
ipcMain.on('AGANI::response-file-save', handleResponseForSave)
|
||||
|
||||
ipcMain.on('AGANI::ask-for-auto-save', e => {
|
||||
const win = BrowserWindow.fromWebContents(e.sender)
|
||||
const { autoSave } = getUserPreference()
|
||||
win.webContents.send('AGANI::auto-save', autoSave)
|
||||
})
|
||||
|
||||
ipcMain.on('AGANI::response-export', handleResponseForExport)
|
||||
|
||||
ipcMain.on('AGANI::close-window', e => {
|
||||
@ -155,6 +163,15 @@ export const saveAs = win => {
|
||||
win.webContents.send('AGANI::ask-file-save-as')
|
||||
}
|
||||
|
||||
export const autoSave = (menuItem, win) => {
|
||||
// TODO
|
||||
export const autoSave = (menuItem, browserWindow) => {
|
||||
const { checked } = menuItem
|
||||
setUserPreference('autoSave', checked)
|
||||
.then(() => {
|
||||
for (const win of windows.values()) {
|
||||
win.webContents.send('AGANI::auto-save', checked)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { ipcMain } from 'electron'
|
||||
import { getMenuItem } from '../utils'
|
||||
import { getMenuItem, setUserPreference } from '../utils'
|
||||
import { windows } from '../createWindow'
|
||||
/**
|
||||
* Set `__static` path to static files in production
|
||||
@ -17,9 +17,15 @@ const THEME_PATH = path.join(__static, '/themes')
|
||||
const themeCSS = {}
|
||||
|
||||
export const selectTheme = (theme, themeCSS) => {
|
||||
for (const win of windows.values()) {
|
||||
win.webContents.send('AGANI::theme', { theme, themeCSS })
|
||||
}
|
||||
setUserPreference('theme', theme)
|
||||
.then(() => {
|
||||
for (const win of windows.values()) {
|
||||
win.webContents.send('AGANI::theme', { theme, themeCSS })
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
const getSelectTheme = () => {
|
||||
|
@ -26,7 +26,7 @@ export const EXTENSION_HASN = {
|
||||
pdf: '.pdf'
|
||||
}
|
||||
|
||||
export const DEFAULT_THEME = 'dark'
|
||||
// export const DEFAULT_THEME = 'dark'
|
||||
|
||||
export const VIEW_MENU_ITEM = {
|
||||
'Source Code Mode': false,
|
||||
|
@ -1,4 +1,7 @@
|
||||
import * as actions from '../actions/file'
|
||||
import { getUserPreference } from '../utils'
|
||||
|
||||
const { autoSave } = getUserPreference()
|
||||
|
||||
export default {
|
||||
label: 'File',
|
||||
@ -37,9 +40,10 @@ export default {
|
||||
}
|
||||
}, {
|
||||
label: 'Auto Save',
|
||||
type: 'radio',
|
||||
type: 'checkbox',
|
||||
checked: autoSave,
|
||||
click (menuItem, browserWindow) {
|
||||
actions.autoSave(browserWindow)
|
||||
actions.autoSave(menuItem, browserWindow)
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
|
@ -1,18 +1,21 @@
|
||||
import * as actions from '../actions/theme'
|
||||
import { getUserPreference } from '../utils'
|
||||
|
||||
const { theme } = getUserPreference()
|
||||
|
||||
export default {
|
||||
label: 'Theme',
|
||||
submenu: [{
|
||||
label: 'Dark',
|
||||
type: 'radio',
|
||||
checked: false,
|
||||
checked: theme === 'dark',
|
||||
click (menuItem, browserWindow) {
|
||||
actions.selectTheme('dark')
|
||||
}
|
||||
}, {
|
||||
label: 'Light',
|
||||
type: 'radio',
|
||||
checked: true,
|
||||
checked: theme === 'light',
|
||||
click (menuItem, browserWindow) {
|
||||
actions.selectTheme('light')
|
||||
}
|
||||
|
@ -1,6 +1,47 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { Menu } from 'electron'
|
||||
|
||||
const JSON_REG = /```json(.+)```/g
|
||||
const preferencePath = path.join(__static, 'preference.md')
|
||||
let PREFERENCE_CACHE = null
|
||||
|
||||
export const getMenuItem = menuName => {
|
||||
const menus = Menu.getApplicationMenu()
|
||||
return menus.items.find(menu => menu.label === menuName)
|
||||
}
|
||||
|
||||
export const getUserPreference = () => {
|
||||
if (PREFERENCE_CACHE) {
|
||||
return PREFERENCE_CACHE
|
||||
} else {
|
||||
const content = fs.readFileSync(preferencePath, 'utf-8')
|
||||
try {
|
||||
const userSetting = JSON_REG.exec(content.replace(/\n/g, ''))[1]
|
||||
PREFERENCE_CACHE = JSON.parse(userSetting)
|
||||
return PREFERENCE_CACHE
|
||||
} catch (err) {
|
||||
// todo notice the user
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const setUserPreference = (key, value) => {
|
||||
const preUserSetting = getUserPreference()
|
||||
const newUserSetting = PREFERENCE_CACHE = Object.assign({}, preUserSetting, { [key]: value })
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const content = fs.readFileSync(preferencePath, 'utf-8')
|
||||
const tokens = content.split('```')
|
||||
const newContent = tokens[0] +
|
||||
'```json\n' +
|
||||
JSON.stringify(newUserSetting, null, 2) +
|
||||
'\n```' +
|
||||
tokens[2]
|
||||
fs.writeFile(preferencePath, newContent, 'utf-8', err => {
|
||||
if (err) reject(err)
|
||||
else resolve(newUserSetting)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
:word-count="wordCount"
|
||||
:theme="theme"
|
||||
:platform="platform"
|
||||
:is-saved="isSaved"
|
||||
></title-bar>
|
||||
<editor
|
||||
:typewriter="typewriter"
|
||||
@ -57,7 +58,7 @@
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'pathname', 'filename', 'windowActive', 'wordCount',
|
||||
'pathname', 'filename', 'isSaved', 'windowActive', 'wordCount',
|
||||
'typewriter', 'focus', 'sourceCode', 'markdown',
|
||||
'cursor', 'theme', 'themeCSS', 'platform'
|
||||
])
|
||||
@ -66,6 +67,7 @@
|
||||
const { dispatch } = this.$store
|
||||
|
||||
dispatch('ASK_FOR_THEME')
|
||||
dispatch('ASK_FOR_AUTO_SAVE')
|
||||
dispatch('ASK_FOR_MODE')
|
||||
dispatch('LISTEN_FOR_CLOSE')
|
||||
dispatch('LISTEN_FOR_SAVE_AS')
|
||||
|
@ -10,6 +10,7 @@
|
||||
</svg>
|
||||
</span>
|
||||
<span :class="[{ 'title-no-drag': platform === 'win32' }]">{{ filename }}</span>
|
||||
<span class="save-dot" :class="{'show': !isSaved}"></span>
|
||||
</div>
|
||||
<div :class="platform === 'win32' ? 'left-toolbar' : 'right-toolbar'">
|
||||
<div v-if="platform === 'win32'" class="windows-titlebar-menu title-no-drag" @click.stop="handleMenuClick">☰</div>
|
||||
@ -48,7 +49,8 @@
|
||||
active: Boolean,
|
||||
wordCount: Object,
|
||||
theme: String,
|
||||
platform: String
|
||||
platform: String,
|
||||
isSaved: Boolean
|
||||
},
|
||||
computed: {
|
||||
paths () {
|
||||
@ -81,7 +83,10 @@
|
||||
},
|
||||
handleMenuClick () {
|
||||
const win = remote.getCurrentWindow()
|
||||
remote.Menu.getApplicationMenu().popup({window: win, x: 23, y: 20})
|
||||
remote
|
||||
.Menu
|
||||
.getApplicationMenu()
|
||||
.popup({ window: win, x: 23, y: 20 })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,9 +126,25 @@
|
||||
text-align: center;
|
||||
transition: all .25s ease-in-out;
|
||||
}
|
||||
|
||||
.active .save-dot {
|
||||
margin-left: 3px;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
background: rgba(242, 134, 94, .7);
|
||||
visibility: hidden;
|
||||
}
|
||||
.active .save-dot.show {
|
||||
visibility: visible;
|
||||
}
|
||||
.title:hover {
|
||||
color: #303133;
|
||||
}
|
||||
.title:hover .save-dot {
|
||||
background: rgb(242, 134, 94);
|
||||
}
|
||||
.right-toolbar {
|
||||
padding: 0 10px;
|
||||
height: 100%;
|
||||
|
@ -16,6 +16,7 @@ const state = {
|
||||
sourceCode: false, // source code mode
|
||||
pathname: '',
|
||||
isSaved: true,
|
||||
autoSave: false,
|
||||
markdown: '',
|
||||
cursor: null,
|
||||
windowActive: true,
|
||||
@ -51,7 +52,7 @@ const mutations = {
|
||||
window.__dirname = path.dirname(pathname)
|
||||
state.pathname = pathname
|
||||
},
|
||||
SET_STATUS (state, status) {
|
||||
SET_SAVE_STATUS (state, status) {
|
||||
state.isSaved = status
|
||||
},
|
||||
SET_MARKDOWN (state, markdown) {
|
||||
@ -62,6 +63,9 @@ const mutations = {
|
||||
},
|
||||
SET_CURSOR (state, cursor) {
|
||||
state.cursor = cursor
|
||||
},
|
||||
SET_AUTO_SAVE (state, autoSave) {
|
||||
state.autoSave = autoSave
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,9 +76,24 @@ const actions = {
|
||||
commit('SET_THEME', themes)
|
||||
})
|
||||
},
|
||||
|
||||
ASK_FOR_AUTO_SAVE ({ commit, state }) {
|
||||
ipcRenderer.send('AGANI::ask-for-auto-save')
|
||||
ipcRenderer.on('AGANI::auto-save', (e, autoSave) => {
|
||||
const { pathname, markdown } = state
|
||||
commit('SET_AUTO_SAVE', autoSave)
|
||||
|
||||
if (autoSave && pathname) {
|
||||
commit('SET_SAVE_STATUS', true)
|
||||
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
SEARCH ({ commit }, value) {
|
||||
commit('SET_SEARCH', value)
|
||||
},
|
||||
|
||||
ASK_FOR_MODE ({ commit }) {
|
||||
ipcRenderer.send('AGANI::ask-for-mode')
|
||||
ipcRenderer.on('AGANI::res-for-mode', (e, modes) => {
|
||||
@ -86,70 +105,86 @@ const actions = {
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
LINTEN_WIN_STATUS ({ commit }) {
|
||||
ipcRenderer.on('AGANI::window-active-status', (e, { status }) => {
|
||||
commit('SET_WIN_STATUS', status)
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_SAVE ({ commit, state }) {
|
||||
ipcRenderer.on('AGANI::ask-file-save', () => {
|
||||
const { pathname, markdown } = state
|
||||
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown })
|
||||
commit('SET_SAVE_STATUS', true)
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_SAVE_AS ({ commit, state }) {
|
||||
ipcRenderer.on('AGANI::ask-file-save-as', () => {
|
||||
const { pathname, markdown } = state
|
||||
ipcRenderer.send('AGANI::response-file-save-as', { pathname, markdown })
|
||||
})
|
||||
},
|
||||
|
||||
GET_FILENAME ({ commit, state }) {
|
||||
ipcRenderer.on('AGANI::set-pathname', (e, { pathname, filename }) => {
|
||||
commit('SET_FILENAME', filename)
|
||||
commit('SET_PATHNAME', pathname)
|
||||
commit('SET_STATUS', true)
|
||||
commit('SET_SAVE_STATUS', true)
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_FILE_LOAD ({ commit, state }) {
|
||||
ipcRenderer.on('AGANI::file-loaded', (e, { file, filename, pathname }) => {
|
||||
commit('SET_FILENAME', filename)
|
||||
commit('SET_PATHNAME', pathname)
|
||||
commit('SET_MARKDOWN', file)
|
||||
commit('SET_STATUS', true)
|
||||
commit('SET_SAVE_STATUS', true)
|
||||
bus.$emit('file-loaded', file)
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_FILE_CHANGE ({ commit, state }) {
|
||||
ipcRenderer.on('AGANI::file-change', (e, { file, filename, pathname }) => {
|
||||
const { windowActive } = state
|
||||
commit('SET_FILENAME', filename)
|
||||
commit('SET_PATHNAME', pathname)
|
||||
commit('SET_MARKDOWN', file)
|
||||
commit('SET_STATUS', true)
|
||||
commit('SET_SAVE_STATUS', true)
|
||||
if (!windowActive) {
|
||||
bus.$emit('file-loaded', file)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
EDITE_FILE ({ commit }) {
|
||||
commit('SET_STATUS', false)
|
||||
commit('SET_SAVE_STATUS', false)
|
||||
},
|
||||
|
||||
EXPORT ({ commit, state }, { type, content }) {
|
||||
const { filename, pathname } = state
|
||||
ipcRenderer.send('AGANI::response-export', { type, content, filename, pathname })
|
||||
},
|
||||
|
||||
SAVE_FILE ({ commit, state }, { markdown, wordCount, cursor }) {
|
||||
const { pathname, autoSave, markdown: oldMarkdown } = state
|
||||
commit('SET_MARKDOWN', markdown)
|
||||
// set word count
|
||||
if (wordCount) commit('SET_WORD_COUNT', wordCount)
|
||||
// set cursor
|
||||
if (cursor) commit('SET_CURSOR', cursor)
|
||||
const { pathname } = state
|
||||
if (pathname) {
|
||||
commit('SET_STATUS', true)
|
||||
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown })
|
||||
} else {
|
||||
commit('SET_STATUS', false)
|
||||
// save to file only when the markdown changed!
|
||||
if (markdown !== oldMarkdown) {
|
||||
if (pathname && autoSave) {
|
||||
commit('SET_SAVE_STATUS', true)
|
||||
ipcRenderer.send('AGANI::response-file-save', { pathname, markdown })
|
||||
} else {
|
||||
commit('SET_SAVE_STATUS', false)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
SELECTION_CHANGE ({ commit }, changes) {
|
||||
const { start, end } = changes
|
||||
if (start.key === end.key && start.block.text) {
|
||||
@ -163,15 +198,18 @@ const actions = {
|
||||
|
||||
ipcRenderer.send('AGANI::selection-change', changes)
|
||||
},
|
||||
|
||||
SELECTION_FORMATS ({ commit }, formats) {
|
||||
ipcRenderer.send('AGANI::selection-formats', formats)
|
||||
},
|
||||
|
||||
// listen for export from main process
|
||||
LISTEN_FOR_EXPORT ({ commit, state }) {
|
||||
ipcRenderer.on('AGANI::export', (e, { type }) => {
|
||||
bus.$emit('export', type)
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_INSERT_IMAGE ({ commit, state }) {
|
||||
ipcRenderer.on('AGANI::INSERT_IMAGE', (e, { filename: imagePath, type }) => {
|
||||
if (type === 'absolute' || type === 'relative') {
|
||||
@ -186,16 +224,19 @@ const actions = {
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_EDIT ({ commit }) {
|
||||
ipcRenderer.on('AGANI::edit', (e, { type }) => {
|
||||
bus.$emit(type)
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_VIEW ({ commit }) {
|
||||
ipcRenderer.on('AGANI::view', (e, data) => {
|
||||
commit('SET_MODE', data)
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_PARAGRAPH_INLINE_STYLE ({ commit }) {
|
||||
ipcRenderer.on('AGANI::paragraph', (e, { type }) => {
|
||||
bus.$emit('paragraph', type)
|
||||
@ -204,6 +245,7 @@ const actions = {
|
||||
bus.$emit('format', type)
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_CLOSE ({ commit, state }) {
|
||||
ipcRenderer.on('AGANI::ask-for-close', e => {
|
||||
const { isSaved, markdown, pathname, filename } = state
|
||||
|
@ -1,6 +1,10 @@
|
||||
#### User Preferences
|
||||
### :bust_in_silhouette:User Preferences
|
||||
|
||||
Edit and save to update preferences:
|
||||
Edit and save to update preferences, You can only change the json bellow!
|
||||
|
||||
- **theme**: *String* `dark` or `light`
|
||||
|
||||
- **autoSave**: *Boolean* `true` or `false`
|
||||
|
||||
```json
|
||||
{
|
||||
@ -9,4 +13,10 @@ Edit and save to update preferences:
|
||||
}
|
||||
```
|
||||
|
||||
More user preferences comming.
|
||||
More user preferences comming soon.
|
||||
|
||||
**Please use `CmdOrCtrl + S` to save your preferences and reload Mark Text to use your setting!**
|
||||
|
||||
|
||||
|
||||
> Your friends at Mark Text
|
||||
|
Loading…
Reference in New Issue
Block a user