Mark Text Preference (#1003)
* dynamic change element-ui theme to our themeColor * add some ui components * add preference doc * add json schema file * update preference.json and schema.json * reset to old commit * rename preference file for rebase * rebase develop * add setting window * user electron-store to store preferences * add themes setting * add select component * add markdown pref * fix: bool and select init value * add font size setting * editor pref * add general preference * search preference * update menu after preference changed * update muya codes * prevent scale setting window * fix: titlebar undefined * update input style * remove window from windowManager after close setting window * remove old docs and preference.md * if a setting window is already created, no need to create another one, just move it to top * rename openFilesInNewWindow to openFileInNewWindow * change aidou runtime * change hideQuickInsertHint by setting page runtime * change autopair runtime * change codefont and codefontfamily dynamic * change default value of autoSave to false * update bulletListMarker * fix style error * add custom titlebar to settings window * add window shadow for Linux and Windows * fix Windows build * fix some typo error * update doc * add default menu and setting menu * fix update menu bug * fix typo * remove mac titlebarstyle * do not need to send titlebarstyle to renderer * fix typo * crash Mark Text if no initial preference.json file * update the path * add showCustomTitleBar prop * set empty settings menu on Linux/Windows + workaround
4
.babelrc
@ -42,8 +42,8 @@
|
||||
}
|
||||
},
|
||||
"plugins": [["component", {
|
||||
"libraryName": "element-ui",
|
||||
"styleLibraryName": "theme-chalk"
|
||||
"style": false,
|
||||
"libraryName": "element-ui"
|
||||
}
|
||||
], "transform-runtime"]
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ const rendererConfig = {
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /(katex|github\-markdown|prism[\-a-z]*|\.theme)\.css$/,
|
||||
test: /(theme\-chalk(?:\/|\\)index|katex|github\-markdown|prism[\-a-z]*|\.theme)\.css$/,
|
||||
use: [
|
||||
'to-string-loader',
|
||||
'css-loader'
|
||||
@ -55,7 +55,7 @@ const rendererConfig = {
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /(katex|github\-markdown|prism[\-a-z]*|\.theme)\.css$/,
|
||||
exclude: /(theme\-chalk(?:\/|\\)index|katex|github\-markdown|prism[\-a-z]*|\.theme)\.css$/,
|
||||
use: [
|
||||
proMode ? MiniCssExtractPlugin.loader : 'style-loader',
|
||||
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
||||
@ -131,6 +131,12 @@ const rendererConfig = {
|
||||
name: 'fonts/[name]--[folder].[ext]'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.md$/,
|
||||
use: [
|
||||
'raw-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
48
doc/PREFERENCE.md
Normal file
@ -0,0 +1,48 @@
|
||||
## Mark Text Preference
|
||||
|
||||
#### General
|
||||
|
||||
| Key | Type | Default Value | Description |
|
||||
| -------------------- | ------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| autoSave | Boolean | ture | Automatically save the content being edited. option value: true, false |
|
||||
| autoSaveDelay | Number | 3000 | The delay in milliseconds after a changed file is saved automatically? 3000 ~10000 |
|
||||
| titleBarStyle | String | csd | The title bar style. the native option will result in a standard gray opaque title bar. `csd` (macOS only), `custom`, `native` |
|
||||
| openFilesInNewWindow | Boolean | false | true, false |
|
||||
| aidou | Boolean | true | Enable aidou. Optional value: true, false |
|
||||
| fileSortBy | String | modified | Sort files in opened folder by `created` time, modified time and title. |
|
||||
| startUp | String | lastState | The action after Mark Text startup, open the last edited content, open the specified folder or blank page, optional value: `lasteState`, `folder`, `blank` |
|
||||
| language | String | en | The language Mark Text use. |
|
||||
|
||||
#### Editor
|
||||
|
||||
| Key | Type | Defaut | Description |
|
||||
| ---------------------- | ------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| fontSize | Number | 16 | Font size in pixels. 12 ~ 32 |
|
||||
| editorFontFamily | String | Open Sans | Font Family |
|
||||
| lineHeight | Number | 1.6 | Line Height |
|
||||
| autoPairBracket | Boolean | true | Automatically brackets when editing |
|
||||
| autoPairMarkdownSyntax | Boolean | true | Autocomplete markdown syntax |
|
||||
| autoPairQuote | Boolean | true | Automatic completion of quotes |
|
||||
| endOfLine | String | default | The newline character used at the end of each line. The default value is default, which will be selected according to your system intelligence. `lf` `crlf` `default` |
|
||||
| textDirection | String | ltr | The writing text direction, optional value: `ltr` or `rtl` |
|
||||
| codeFontSize | Number | 14 | Font size on code block, the range is 12 ~ 28 |
|
||||
| codeFontFamily | String | `DejaVu Sans Mono` | Code font family |
|
||||
| hideQuickInsertHint | Boolean | false | Hide hint for quickly creating paragraphs |
|
||||
| imageDropAction | String | folder | The default behavior after paste or drag the image to Mark Text, upload it to the image cloud (if configured), move to the specified folder, insert the path |
|
||||
|
||||
#### Markdown
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| preferLooseListItem | Boolean | true | The preferred list type. |
|
||||
| bulletListMarker | String | `-` | The preferred marker used in bullet list, optional value: `-`, `*` `+` |
|
||||
| orderListDelimiter | String | `.` | The preferred delimiter used in order list, optional value: `.` `)` |
|
||||
| preferHeadingStyle | String | `atx` | The preferred heading style in Mark Text, optional value `atx` `setext`, [more info](https://spec.commonmark.org/0.29/#atx-headings) |
|
||||
| tabSize | Number | 4 | The number of spaces a tab is equal to |
|
||||
| listIndentation | String | 1 | The list indentation of sub list items or paragraphs, optional value `dfm`, `tab` or number 1~4 |
|
||||
|
||||
#### Theme
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ----- | ------ | ------- | -------------------------------------------------------------- |
|
||||
| theme | String | light | `dark` `graphite` `material-dark` `one-dark` `light` `ulysses` |
|
@ -1,42 +0,0 @@
|
||||
# Settings
|
||||
|
||||
## Options
|
||||
|
||||
### Editor
|
||||
|
||||
- **fontSize**: The editor font size.
|
||||
- **editorFontFamily**: The editor font family name.
|
||||
- **codeFontSize**: The code block font size.
|
||||
- **codeFontFamily**: The code block font family name.
|
||||
- **lineHeight**: The line height of the editor.
|
||||
- **tabSize**: The number of spaces a tab is equal to.
|
||||
- **listIndentation**: The list indentation of sub list items or paragraphs (`"dfm"`, `"tab"` or number `1-4`)
|
||||
- `dfm`: Each subsequent paragraph in a list item must be indented by either 4 spaces or one tab, we are using 4 spaces (used by Bitbucket and Daring Fireball Markdown Spec).
|
||||
- `number`: Dynamic indent subsequent paragraphs by the given number (1-4) plus list marker width (default).
|
||||
- **autoPairBracket**: If `true` the editor automatically closes brackets.
|
||||
- **autoPairMarkdownSyntax**: If `true` the editor automatically closes inline markdown like `*` or `_`.
|
||||
- **autoPairQuote**: If `true` the editor automatically closes quotes (`'` and `"`).
|
||||
- **hideQuickInsertHint**: If `true` the editor hides the quick insert hint.
|
||||
- **preferLooseListItem**: The preferred list style. If `true` a loose list is preferred otherwise a tight list.
|
||||
- **bulletListMarker**: The preferred list item bullet (`+`,`-` or `*`).
|
||||
|
||||
### Files
|
||||
|
||||
- **autoSave**: Automatically saves the file after editing.
|
||||
- **endOfLine**: The default end of line character (`lf`, `crlf` or `default`).
|
||||
|
||||
### Window
|
||||
|
||||
- **theme**: Specifies the theme (`dark`, `graphite`, `material-dark`, `one-dark`, `light` or `ulysses`).
|
||||
- **textDirection**: The editor text direction (`ltr` or `rtl`).
|
||||
- **openFilesInNewWindow**: If `true` files should opened in a new window.
|
||||
- **titleBarStyle**: Specifies the title bar (`csd` (macOS only), `custom` or `native`).
|
||||
|
||||
### Misc
|
||||
|
||||
- **aidou**: Show aidou menu entry.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- **lightColor**
|
||||
- **darkColor**
|
@ -176,6 +176,7 @@
|
||||
"dragula": "^3.7.2",
|
||||
"electron-is-accelerator": "^0.1.2",
|
||||
"electron-log": "^3.0.5",
|
||||
"electron-store": "^3.2.0",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"element-resize-detector": "^1.2.0",
|
||||
"element-ui": "^2.8.2",
|
||||
@ -203,6 +204,7 @@
|
||||
"view-image": "^0.0.1",
|
||||
"vue": "^2.6.10",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-router": "^3.0.6",
|
||||
"vuex": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -265,6 +267,7 @@
|
||||
"node-loader": "^0.6.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-preset-env": "^6.6.0",
|
||||
"raw-loader": "^2.0.0",
|
||||
"require-dir": "^1.2.0",
|
||||
"spectron": "^5.0.0",
|
||||
"style-loader": "^0.23.1",
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { app, ipcMain, systemPreferences } from 'electron'
|
||||
import { isOsx } from '../config'
|
||||
import { isLinux, isOsx } from '../config'
|
||||
import { isDirectory, isMarkdownFileOrLink, normalizeAndResolvePath } from '../filesystem'
|
||||
import { getMenuItemById } from '../menu'
|
||||
import { selectTheme } from '../menu/actions/theme'
|
||||
import { dockMenu } from '../menu/templates'
|
||||
import { watchers } from '../utils/imagePathAutoComplement'
|
||||
import EditorWindow from '../windows/editor'
|
||||
import SettingWindow from '../windows/setting'
|
||||
import { WindowType } from './windowManager'
|
||||
|
||||
class App {
|
||||
|
||||
@ -182,6 +184,17 @@ class App {
|
||||
this._accessor.menu.setActiveWindow(editor.id)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a new setting window.
|
||||
*/
|
||||
createSettingWindow () {
|
||||
const setting = new SettingWindow(this._accessor)
|
||||
setting.createWindow()
|
||||
this._windowManager.add(setting)
|
||||
if (this._windowManager.windowCount === 1) {
|
||||
this._accessor.menu.setActiveWindow(setting.id)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(sessions): ...
|
||||
// // Make Mark Text a single instance application.
|
||||
@ -205,8 +218,18 @@ class App {
|
||||
})
|
||||
|
||||
ipcMain.on('app-create-settings-window', () => {
|
||||
const { paths } = this._accessor
|
||||
this.createEditorWindow(paths.preferencesFilePath)
|
||||
const settingWins = this._windowManager.windowsOfType(WindowType.SETTING)
|
||||
if (settingWins.length >= 1) {
|
||||
// A setting window is already created
|
||||
const browserSettingWindow = settingWins[0].win.browserWindow
|
||||
if (isLinux) {
|
||||
browserSettingWindow.focus()
|
||||
} else {
|
||||
browserSettingWindow.moveTop()
|
||||
}
|
||||
return
|
||||
}
|
||||
this.createSettingWindow()
|
||||
})
|
||||
|
||||
// ipcMain.on('app-open-file', filePath => {
|
||||
|
@ -11,10 +11,11 @@ import Watcher from '../filesystem/watcher'
|
||||
* @property {WindowType} type The window type.
|
||||
*/
|
||||
|
||||
// Currently it makes no sense because we have only one (editor) window but we
|
||||
// will add more windows like settings and worker windows.
|
||||
// Window type marktext support.
|
||||
export const WindowType = {
|
||||
EDITOR: 0
|
||||
BASE: 'base', // You shold never create a `BASE` window.
|
||||
EDITOR: 'editor',
|
||||
SETTING: 'setting'
|
||||
}
|
||||
|
||||
class WindowActivityList {
|
||||
@ -86,8 +87,7 @@ class WindowManager extends EventEmitter {
|
||||
this._windows.set(window.id, window)
|
||||
|
||||
if (!this._appMenu.has(window.id)) {
|
||||
// TODO: Build a default menu for macOS.
|
||||
this._appMenu.addMenu(window.id, null)
|
||||
this._appMenu.addDefaultMenu(window.id)
|
||||
}
|
||||
|
||||
if (this.windowCount === 1) {
|
||||
@ -175,6 +175,28 @@ class WindowManager extends EventEmitter {
|
||||
return this._windows.size
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {type} type the WindowType one of ['base', 'editor', 'setting']
|
||||
* Return the windows of the given {type}
|
||||
*/
|
||||
windowsOfType (type) {
|
||||
if (!WindowType[type.toUpperCase()]) {
|
||||
console.error(`${type} is not a valid window type.`)
|
||||
}
|
||||
const { windows } = this
|
||||
const result = []
|
||||
for (var [key, value] of windows) {
|
||||
if (value.type === type) {
|
||||
result.push({
|
||||
id: key,
|
||||
win: value
|
||||
})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// --- helper ---------------------------------
|
||||
|
||||
closeWatcher () {
|
||||
@ -280,8 +302,14 @@ class WindowManager extends EventEmitter {
|
||||
})
|
||||
|
||||
ipcMain.on('broadcast-preferences-changed', prefs => {
|
||||
for (const { browserWindow } of this._windows.values()) {
|
||||
browserWindow.webContents.send('AGANI::user-preference', prefs)
|
||||
// We can not dynamic change the title bar style, so do not need to send it to renderer.
|
||||
if (typeof prefs.titleBarStyle !== 'undefined') {
|
||||
delete prefs.titleBarStyle
|
||||
}
|
||||
if (Object.keys(prefs).length > 0) {
|
||||
for (const { browserWindow } of this._windows.values()) {
|
||||
browserWindow.webContents.send('AGANI::user-preference', prefs)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -15,6 +15,26 @@ export const defaultWinOptions = {
|
||||
titleBarStyle: 'hiddenInset'
|
||||
}
|
||||
|
||||
export const defaultPreferenceWinOptions = {
|
||||
width: 950,
|
||||
height: 650,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
webSecurity: false,
|
||||
},
|
||||
fullscreenable: false,
|
||||
fullscreen: false,
|
||||
resizable: false,
|
||||
minimizable: false,
|
||||
maximizable: false,
|
||||
useContentSize: true,
|
||||
show: false,
|
||||
frame: false,
|
||||
thickFrame: !isOsx,
|
||||
titleBarStyle: 'hiddenInset',
|
||||
center: true
|
||||
}
|
||||
|
||||
export const EXTENSIONS = [
|
||||
'markdown',
|
||||
'mdown',
|
||||
@ -68,7 +88,7 @@ export const EXTENSION_HASN = {
|
||||
pdf: '.pdf'
|
||||
}
|
||||
|
||||
export const TITLE_BAR_HEIGHT = isOsx ? 21 : 25
|
||||
export const TITLE_BAR_HEIGHT = isOsx ? 21 : 32
|
||||
export const LINE_ENDING_REG = /(?:\r\n|\n)/g
|
||||
export const LF_LINE_ENDING_REG = /(?:[^\r]\n)|(?:^\n$)/
|
||||
export const CRLF_LINE_ENDING_REG = /\r\n/
|
||||
|
@ -17,10 +17,6 @@ export const typeMode = (win, type, item) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const changeFont = win => {
|
||||
win.webContents.send('AGANI::font-setting')
|
||||
}
|
||||
|
||||
export const layout = (item, win, type) => {
|
||||
win.webContents.send('AGANI::listen-for-view-layout', { [type]: item.checked })
|
||||
}
|
||||
|
@ -2,9 +2,10 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { app, ipcMain, Menu } from 'electron'
|
||||
import log from 'electron-log'
|
||||
import { isLinux } from '../config'
|
||||
import { ensureDirSync, isDirectory, isFile } from '../filesystem'
|
||||
import { parseMenu } from '../keyboard/shortcutHandler'
|
||||
import configureMenu from '../menu/templates'
|
||||
import configureMenu, { configSettingMenu } from '../menu/templates'
|
||||
|
||||
class AppMenu {
|
||||
|
||||
@ -92,9 +93,16 @@ class AppMenu {
|
||||
fs.writeFileSync(RECENTS_PATH, json, 'utf-8')
|
||||
}
|
||||
|
||||
addMenu (windowId, menu=null) {
|
||||
addDefaultMenu (windowId) {
|
||||
const { windowMenus } = this
|
||||
windowMenus.set(windowId, { menu })
|
||||
const menu = this.buildSettingMenu() // Setting menu is also the fallback menu.
|
||||
windowMenus.set(windowId, menu)
|
||||
}
|
||||
|
||||
addSettingMenu (window) {
|
||||
const { windowMenus } = this
|
||||
const menu = this.buildSettingMenu()
|
||||
windowMenus.set(window.id, menu)
|
||||
}
|
||||
|
||||
addEditorMenu (window) {
|
||||
@ -146,7 +154,7 @@ class AppMenu {
|
||||
setActiveWindow (windowId) {
|
||||
if (this.activeWindowId !== windowId) {
|
||||
// Change application menu to the current window menu.
|
||||
Menu.setApplicationMenu(this.getWindowMenuById(windowId))
|
||||
this._setApplicationMenu(this.getWindowMenuById(windowId))
|
||||
this.activeWindowId = windowId
|
||||
}
|
||||
}
|
||||
@ -170,6 +178,15 @@ class AppMenu {
|
||||
}
|
||||
}
|
||||
|
||||
buildSettingMenu () {
|
||||
if (this.isOsx) {
|
||||
const menuTemplate = configSettingMenu(this._keybindings)
|
||||
const menu = Menu.buildFromTemplate(menuTemplate)
|
||||
return { menu }
|
||||
}
|
||||
return { menu: null }
|
||||
}
|
||||
|
||||
updateAppMenu (recentUsedDocuments) {
|
||||
if (!recentUsedDocuments) {
|
||||
recentUsedDocuments = this.getRecentlyUsedDocuments()
|
||||
@ -197,7 +214,7 @@ class AppMenu {
|
||||
// update application menu if necessary
|
||||
const { activeWindowId } = this
|
||||
if (activeWindowId === key) {
|
||||
Menu.setApplicationMenu(newMenu)
|
||||
this._setApplicationMenu(newMenu)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -212,6 +229,55 @@ class AppMenu {
|
||||
menu.checked = flag
|
||||
}
|
||||
|
||||
updateThemeMenu = theme => {
|
||||
this.windowMenus.forEach((value, key) => {
|
||||
const { menu } = value
|
||||
const themeMenus = menu.getMenuItemById('themeMenu')
|
||||
if (!themeMenus) {
|
||||
return
|
||||
}
|
||||
themeMenus.submenu.items.forEach(item => (item.checked = false))
|
||||
themeMenus.submenu.items
|
||||
.forEach(item => {
|
||||
if (item.id && item.id === theme) {
|
||||
item.checked = true
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
updateAutoSaveMenu = autoSave => {
|
||||
this.windowMenus.forEach((value, key) => {
|
||||
const { menu } = value
|
||||
const autoSaveMenu = menu.getMenuItemById('autoSaveMenuItem')
|
||||
if (!autoSaveMenu) {
|
||||
return
|
||||
}
|
||||
autoSaveMenu.checked = autoSave
|
||||
})
|
||||
}
|
||||
|
||||
updateAidouMenu = bool => {
|
||||
this.windowMenus.forEach((value, key) => {
|
||||
const { menu } = value
|
||||
const aidouMenu = menu.getMenuItemById('aidou')
|
||||
if (!aidouMenu) {
|
||||
return
|
||||
}
|
||||
aidouMenu.visible = bool
|
||||
})
|
||||
}
|
||||
|
||||
_setApplicationMenu (menu) {
|
||||
if (isLinux && !menu) {
|
||||
// WORKAROUND for Electron#16521: We cannot hide the (application) menu on Linux.
|
||||
const dummyMenu = Menu.buildFromTemplate([])
|
||||
Menu.setApplicationMenu(dummyMenu)
|
||||
} else {
|
||||
Menu.setApplicationMenu(menu)
|
||||
}
|
||||
}
|
||||
|
||||
_listenForIpcMain () {
|
||||
ipcMain.on('mt::add-recently-used-document', (e, pathname) => {
|
||||
this.addRecentlyUsedDocument(pathname)
|
||||
@ -220,6 +286,18 @@ class AppMenu {
|
||||
ipcMain.on('menu-clear-recently-used', () => {
|
||||
this.clearRecentlyUsedDocuments()
|
||||
})
|
||||
|
||||
ipcMain.on('broadcast-preferences-changed', prefs => {
|
||||
if (prefs.theme !== undefined) {
|
||||
this.updateThemeMenu(prefs.theme)
|
||||
}
|
||||
if (prefs.autoSave !== undefined) {
|
||||
this.updateAutoSaveMenu(prefs.autoSave)
|
||||
}
|
||||
if (prefs.aidou !== undefined) {
|
||||
this.updateAidouMenu(prefs.aidou)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,7 @@ export default function (keybindings, userPreference) {
|
||||
}, {
|
||||
label: 'Aidou',
|
||||
visible: aidou,
|
||||
id: 'aidou',
|
||||
accelerator: keybindings.getAccelerator('editAidou'),
|
||||
click (menuItem, browserWindow) {
|
||||
actions.edit(browserWindow, 'aidou')
|
||||
|
@ -101,6 +101,7 @@ export default function (keybindings, userPreference, recentlyUsedFiles) {
|
||||
label: 'Auto Save',
|
||||
type: 'checkbox',
|
||||
checked: autoSave,
|
||||
id: 'autoSaveMenuItem',
|
||||
click (menuItem, browserWindow) {
|
||||
actions.autoSave(menuItem, browserWindow)
|
||||
}
|
||||
|
@ -10,6 +10,18 @@ import theme from './theme'
|
||||
|
||||
export dockMenu from './dock'
|
||||
|
||||
/**
|
||||
* Create the setting window menu.
|
||||
*
|
||||
* @param {Keybindings} keybindings The keybindings instance
|
||||
*/
|
||||
export const configSettingMenu = (keybindings) => {
|
||||
return [
|
||||
...(process.platform === 'darwin' ? [ marktext(keybindings) ] : []),
|
||||
help()
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the application menu for the editor window.
|
||||
*
|
||||
|
@ -14,14 +14,6 @@ export default function (keybindings) {
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Font...',
|
||||
accelerator: keybindings.getAccelerator('viewChangeFont'),
|
||||
click (item, browserWindow) {
|
||||
actions.changeFont(browserWindow)
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
id: 'sourceCodeModeMenuItem',
|
||||
label: 'Source Code Mode',
|
||||
|
@ -1,12 +1,14 @@
|
||||
import fse from 'fs-extra'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import EventEmitter from 'events'
|
||||
import Store from 'electron-store'
|
||||
import { BrowserWindow, ipcMain, systemPreferences } from 'electron'
|
||||
import log from 'electron-log'
|
||||
import { isOsx, isWindows } from '../config'
|
||||
import { ensureDirSync } from '../filesystem'
|
||||
import { hasSameKeys } from '../utils'
|
||||
import { getStringRegKey, winHKEY } from '../platform/win32/registry.js'
|
||||
import schema from './schema'
|
||||
|
||||
const isDarkSystemMode = () => {
|
||||
if (isOsx) {
|
||||
@ -19,6 +21,8 @@ const isDarkSystemMode = () => {
|
||||
return false
|
||||
}
|
||||
|
||||
const PREFERENCES_FILE_NAME = 'preferences'
|
||||
|
||||
class Preference extends EventEmitter {
|
||||
|
||||
/**
|
||||
@ -27,33 +31,38 @@ class Preference extends EventEmitter {
|
||||
constructor (paths) {
|
||||
super()
|
||||
|
||||
const { userDataPath, preferencesFilePath } = paths
|
||||
this._userDataPath = userDataPath
|
||||
const { preferencesPath } = paths
|
||||
this.preferencesPath = preferencesPath
|
||||
this.hasPreferencesFile = fs.existsSync(path.join(this.preferencesPath, `./${PREFERENCES_FILE_NAME}.json`))
|
||||
this.store = new Store({
|
||||
schema,
|
||||
name: PREFERENCES_FILE_NAME
|
||||
})
|
||||
|
||||
this.cache = null
|
||||
this.staticPath = path.join(__static, 'preference.md')
|
||||
this.settingsPath = preferencesFilePath
|
||||
this.staticPath = path.join(__static, 'preference.json')
|
||||
this.init()
|
||||
}
|
||||
|
||||
init () {
|
||||
const { settingsPath, staticPath } = this
|
||||
const defaultSettings = this.loadJson(staticPath)
|
||||
let userSetting = null
|
||||
|
||||
// Try to load settings or write default settings if file doesn't exists.
|
||||
if (!fs.existsSync(settingsPath) || !this.loadJson(settingsPath)) {
|
||||
ensureDirSync(this._userDataPath)
|
||||
const content = fs.readFileSync(staticPath, 'utf-8')
|
||||
fs.writeFileSync(settingsPath, content, 'utf-8')
|
||||
|
||||
userSetting = this.loadJson(settingsPath)
|
||||
init = () => {
|
||||
let defaultSettings = null
|
||||
try {
|
||||
defaultSettings = fse.readJsonSync(this.staticPath)
|
||||
if (isDarkSystemMode()) {
|
||||
userSetting.theme = 'dark'
|
||||
defaultSettings.theme = 'dark'
|
||||
}
|
||||
this.validateSettings(userSetting)
|
||||
} catch (err) {
|
||||
log(err)
|
||||
}
|
||||
|
||||
if (!defaultSettings) {
|
||||
throw new Error('Can not load static preference.json file')
|
||||
}
|
||||
|
||||
// I don't know why `this.store.size` is 3 when first load, so I just check file existed.
|
||||
if (!this.hasPreferencesFile) {
|
||||
this.store.set(defaultSettings)
|
||||
} else {
|
||||
userSetting = this.loadJson(settingsPath)
|
||||
let userSetting = this.getAll()
|
||||
|
||||
// Update outdated settings
|
||||
const requiresUpdate = !hasSameKeys(defaultSettings, userSetting)
|
||||
@ -70,35 +79,24 @@ class Preference extends EventEmitter {
|
||||
userSetting[key] = defaultSettings[key]
|
||||
}
|
||||
}
|
||||
this.validateSettings(userSetting)
|
||||
this.writeJson(userSetting, false)
|
||||
.catch(log.error)
|
||||
} else {
|
||||
this.validateSettings(userSetting)
|
||||
this.store.set(userSetting)
|
||||
}
|
||||
}
|
||||
|
||||
if (!userSetting) {
|
||||
console.error('ERROR: Cannot load settings.')
|
||||
userSetting = defaultSettings
|
||||
this.validateSettings(userSetting)
|
||||
}
|
||||
|
||||
this.cache = userSetting
|
||||
this.emit('loaded', userSetting)
|
||||
|
||||
this._listenForIpcMain()
|
||||
}
|
||||
|
||||
getAll () {
|
||||
return this.cache
|
||||
return this.store.store
|
||||
}
|
||||
|
||||
setItem (key, value) {
|
||||
const preUserSetting = this.getAll()
|
||||
const newUserSetting = this.cache = Object.assign({}, preUserSetting, { [key]: value })
|
||||
this.emit('entry-changed', key, value)
|
||||
return this.writeJson(newUserSetting)
|
||||
ipcMain.emit('broadcast-preferences-changed', { [key]: value })
|
||||
return this.store.set(key, value)
|
||||
}
|
||||
|
||||
getItem (key) {
|
||||
return this.store.get(key)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,123 +110,25 @@ class Preference extends EventEmitter {
|
||||
return
|
||||
}
|
||||
|
||||
const preUserSetting = this.getAll()
|
||||
const newUserSetting = this.cache = Object.assign({}, preUserSetting, settings)
|
||||
|
||||
Object.keys(settings).map(key => {
|
||||
this.emit('entry-changed', key, settings[key])
|
||||
})
|
||||
|
||||
return this.writeJson(newUserSetting)
|
||||
}
|
||||
|
||||
loadJson (filePath) {
|
||||
const JSON_REG = /```json(.+)```/g
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
const userSetting = JSON_REG.exec(content.replace(/(?:\r\n|\n)/g, ''))[1]
|
||||
return JSON.parse(userSetting)
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
writeJson (json, async = true) {
|
||||
const { settingsPath } = this
|
||||
return new Promise((resolve, reject) => {
|
||||
const content = fs.readFileSync(this.staticPath, 'utf-8')
|
||||
const tokens = content.split('```')
|
||||
const newContent = tokens[0] +
|
||||
'```json\n' +
|
||||
JSON.stringify(json, null, 2) +
|
||||
'\n```' +
|
||||
tokens[2]
|
||||
if (async) {
|
||||
fs.writeFile(settingsPath, newContent, 'utf-8', err => {
|
||||
if (err) reject(err)
|
||||
else resolve(json)
|
||||
})
|
||||
} else {
|
||||
fs.writeFileSync(settingsPath, newContent, 'utf-8')
|
||||
resolve(json)
|
||||
}
|
||||
this.setItem(key, settings[key])
|
||||
})
|
||||
}
|
||||
|
||||
getPreferedEOL () {
|
||||
const { endOfLine } = this.getAll()
|
||||
const endOfLine = this.getItem('endOfLine')
|
||||
if (endOfLine === 'lf') {
|
||||
return 'lf'
|
||||
}
|
||||
return endOfLine === 'crlf' || isWindows ? 'crlf' : 'lf'
|
||||
}
|
||||
|
||||
/**
|
||||
* workaround for issue #265
|
||||
* expects: settings != null
|
||||
* @param {Object} settings preferences object
|
||||
*/
|
||||
validateSettings (settings) {
|
||||
if (!settings) {
|
||||
log.warn('Broken settings detected: invalid settings object.')
|
||||
return
|
||||
}
|
||||
exportJSON () {
|
||||
// todo
|
||||
}
|
||||
|
||||
let brokenSettings = false
|
||||
if (!settings.theme || (settings.theme && !/^(?:dark|graphite|material-dark|one-dark|light|ulysses)$/.test(settings.theme))) {
|
||||
brokenSettings = true
|
||||
settings.theme = 'light'
|
||||
}
|
||||
|
||||
if (!settings.codeFontFamily || typeof settings.codeFontFamily !== 'string' || settings.codeFontFamily.length > 60) {
|
||||
settings.codeFontFamily = 'DejaVu Sans Mono'
|
||||
}
|
||||
if (!settings.codeFontSize || typeof settings.codeFontSize !== 'string' || settings.codeFontFamily.length > 10) {
|
||||
settings.codeFontSize = '14px'
|
||||
}
|
||||
|
||||
if (!settings.endOfLine || !/^(?:lf|crlf)$/.test(settings.endOfLine)) {
|
||||
settings.endOfLine = isWindows ? 'crlf' : 'lf'
|
||||
}
|
||||
|
||||
if (!settings.bulletListMarker ||
|
||||
(settings.bulletListMarker && !/^(?:\+|-|\*)$/.test(settings.bulletListMarker))) {
|
||||
brokenSettings = true
|
||||
settings.bulletListMarker = '-'
|
||||
}
|
||||
|
||||
if (!settings.titleBarStyle || !/^(?:native|csd|custom)$/.test(settings.titleBarStyle)) {
|
||||
settings.titleBarStyle = 'csd'
|
||||
}
|
||||
|
||||
if (!settings.tabSize || typeof settings.tabSize !== 'number') {
|
||||
settings.tabSize = 4
|
||||
} else if (settings.tabSize < 1) {
|
||||
settings.tabSize = 1
|
||||
} else if (settings.tabSize > 4) {
|
||||
settings.tabSize = 4
|
||||
}
|
||||
|
||||
if (!settings.listIndentation) {
|
||||
settings.listIndentation = 1
|
||||
} else if (typeof settings.listIndentation === 'number') {
|
||||
if (settings.listIndentation < 1 || settings.listIndentation > 4) {
|
||||
settings.listIndentation = 1
|
||||
}
|
||||
} else if (settings.listIndentation !== 'dfm') {
|
||||
settings.listIndentation = 1
|
||||
}
|
||||
|
||||
if (brokenSettings) {
|
||||
log.warn('Broken settings detected; fallback to default value(s).')
|
||||
}
|
||||
|
||||
// Currently no CSD is available on Linux and Windows (GH#690)
|
||||
const titleBarStyle = settings.titleBarStyle.toLowerCase()
|
||||
if (!isOsx && titleBarStyle === 'csd') {
|
||||
settings.titleBarStyle = 'custom'
|
||||
}
|
||||
importJSON () {
|
||||
// todo
|
||||
}
|
||||
|
||||
_listenForIpcMain () {
|
||||
@ -238,9 +138,7 @@ class Preference extends EventEmitter {
|
||||
})
|
||||
|
||||
ipcMain.on('mt::set-user-preference', (e, settings) => {
|
||||
this.setItems(settings).then(() => {
|
||||
ipcMain.emit('broadcast-preferences-changed', settings)
|
||||
}).catch(log.error)
|
||||
this.setItems(settings)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
171
src/main/preferences/schema.json
Normal file
@ -0,0 +1,171 @@
|
||||
{
|
||||
"autoSave": {
|
||||
"description": "General--Automatically save the content being edited.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"autoSaveDelay": {
|
||||
"description": "General--How long do you want to save your document(ms)?",
|
||||
"type": "number",
|
||||
"minimum": 500
|
||||
},
|
||||
"titleBarStyle": {
|
||||
"description": "General--The title bar style (Windows and Linux system only).",
|
||||
"enum": [
|
||||
"custom",
|
||||
"native"
|
||||
]
|
||||
},
|
||||
"openFilesInNewWindow": {
|
||||
"description": "General--Open file in new window",
|
||||
"type": "boolean"
|
||||
},
|
||||
"aidou": {
|
||||
"description": "General--Enable aidou",
|
||||
"type": "boolean"
|
||||
},
|
||||
"fileSortBy": {
|
||||
"description": "General--Sort files in opened folder by created time, modified time and title.",
|
||||
"enum": [
|
||||
"modified",
|
||||
"created",
|
||||
"title"
|
||||
]
|
||||
},
|
||||
"startUp": {
|
||||
"description": "General--The action after Mark Text startup, open the last edited content, open the specified folder or blank page",
|
||||
"enum": [
|
||||
"folder",
|
||||
"lastState",
|
||||
"blank"
|
||||
]
|
||||
},
|
||||
"language": {
|
||||
"description": "General--The language Mark Text use.",
|
||||
"type": "string"
|
||||
},
|
||||
"editorFontFamily": {
|
||||
"description": "Editor--editor font family",
|
||||
"enum": [
|
||||
"Open Sans",
|
||||
"Clear Sans",
|
||||
"Helvetica Neue",
|
||||
"Helvetica",
|
||||
"Arial",
|
||||
"sans-serif"
|
||||
]
|
||||
},
|
||||
"fontSize": {
|
||||
"description": "Editor--Font size in pixels",
|
||||
"type": "number",
|
||||
"maximum": 32,
|
||||
"minimum": 12,
|
||||
"default": 16
|
||||
},
|
||||
"lineHeight": {
|
||||
"description": "Editor--Line Height",
|
||||
"type": "number",
|
||||
"maximum": 2,
|
||||
"minimum": 1.2,
|
||||
"default": 1.6
|
||||
},
|
||||
"codeFontSize": {
|
||||
"description": "Editor--Font size in code Block, the range is 12 ~ 18",
|
||||
"type": "number",
|
||||
"maximum": 28,
|
||||
"minimum": 12,
|
||||
"default": 14
|
||||
},
|
||||
"codeFontFamily": {
|
||||
"description": "Editor--Font family used in code block",
|
||||
"enum": [
|
||||
"DejaVu Sans Mono",
|
||||
"Source Code Pro",
|
||||
"Droid Sans Mono",
|
||||
"monospace"
|
||||
]
|
||||
},
|
||||
"autoPairBracket": {
|
||||
"description": "Editor--Automatically brackets when editing",
|
||||
"type": "boolean"
|
||||
},
|
||||
"autoPairMarkdownSyntax": {
|
||||
"description": "Editor--Autocomplete markdown syntax",
|
||||
"type": "boolean"
|
||||
},
|
||||
"autoPairQuote": {
|
||||
"description": "Editor--Automatic completion of quotes",
|
||||
"type": "boolean"
|
||||
},
|
||||
"endOfLine": {
|
||||
"description": "Editor--The newline character used at the end of each line. The default value is default, which will be selected according to your system intelligence.",
|
||||
"enum": [
|
||||
"default",
|
||||
"lf",
|
||||
"crlf"
|
||||
]
|
||||
},
|
||||
"textDirection": {
|
||||
"description": "Editor--The writing text direction",
|
||||
"enum": [
|
||||
"ltr",
|
||||
"rtl"
|
||||
]
|
||||
},
|
||||
"hideQuickInsertHint": {
|
||||
"description": "Editor--Hide hint for quickly creating paragraphs",
|
||||
"type": "boolean"
|
||||
},
|
||||
"imageDropAction": {
|
||||
"description": "Editor--The default behavior after paste or drag the image to Mark Text",
|
||||
"enum": [
|
||||
"upload",
|
||||
"folder",
|
||||
"path"
|
||||
]
|
||||
},
|
||||
"preferLooseListItem": {
|
||||
"description": "Markdown--The preferred list type",
|
||||
"type": "boolean"
|
||||
},
|
||||
"bulletListMarker": {
|
||||
"description": "Markdown--The marker used in bullet list",
|
||||
"enum": [
|
||||
"-",
|
||||
"*",
|
||||
"+"
|
||||
]
|
||||
},
|
||||
"orderListDelimiter": {
|
||||
"description": "Markdown--The dilimiter used in order list",
|
||||
"enum": [
|
||||
".",
|
||||
")"
|
||||
]
|
||||
},
|
||||
"preferHeadingStyle": {
|
||||
"description": "Markdown--The preferred heading style in Mark Text",
|
||||
"enum": [
|
||||
"atx",
|
||||
"setext"
|
||||
]
|
||||
},
|
||||
"tabSize": {
|
||||
"description": "Markdown--Replace the tab with x spaces",
|
||||
"type": "number"
|
||||
},
|
||||
"listIndentation": {
|
||||
"description": "Markdown--Select the indent of list",
|
||||
"enum": [
|
||||
"dfm",
|
||||
"tab",
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
},
|
||||
"theme": {
|
||||
"description": "Theme--Select the theme used in Mark Text",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
57
src/main/windows/base.js
Normal file
@ -0,0 +1,57 @@
|
||||
import EventEmitter from 'events'
|
||||
import { WindowType } from '../app/windowManager'
|
||||
|
||||
class BaseWindow extends EventEmitter {
|
||||
|
||||
/**
|
||||
* @param {Accessor} accessor The application accessor for application instances.
|
||||
*/
|
||||
constructor (accessor) {
|
||||
super()
|
||||
|
||||
this._accessor = accessor
|
||||
this.type = WindowType.BASE
|
||||
this.id = null
|
||||
this.browserWindow = null
|
||||
this.quitting = false
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.quitting = true
|
||||
this.emit('bye')
|
||||
|
||||
this.removeAllListeners()
|
||||
this.browserWindow.destroy()
|
||||
this.browserWindow = null
|
||||
this.id = null
|
||||
}
|
||||
|
||||
// --- private ---------------------------------
|
||||
_buildUrlWithSettings (windowId, env, userPreference) {
|
||||
// NOTE: Only send absolutely necessary values. Theme and titlebar settings
|
||||
// are sended because we delay load the preferences.
|
||||
const { type } = this
|
||||
const { debug, paths } = env
|
||||
const { codeFontFamily, codeFontSize, theme, titleBarStyle } = userPreference.getAll()
|
||||
|
||||
const baseUrl = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9091`
|
||||
: `file://${__dirname}/index.html`
|
||||
|
||||
const url = new URL(baseUrl)
|
||||
url.searchParams.set('udp', paths.userDataPath)
|
||||
url.searchParams.set('debug', debug ? '1' : '0')
|
||||
url.searchParams.set('wid', windowId)
|
||||
url.searchParams.set('type', type)
|
||||
|
||||
// Settings
|
||||
url.searchParams.set('cff', codeFontFamily)
|
||||
url.searchParams.set('cfs', codeFontSize)
|
||||
url.searchParams.set('theme', theme)
|
||||
url.searchParams.set('tbs', titleBarStyle)
|
||||
|
||||
return url.toString()
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseWindow
|
@ -1,28 +1,22 @@
|
||||
import path from 'path'
|
||||
import EventEmitter from 'events'
|
||||
import BaseWindow from './base'
|
||||
import { BrowserWindow, ipcMain } from 'electron'
|
||||
import log from 'electron-log'
|
||||
import windowStateKeeper from 'electron-window-state'
|
||||
import { WindowType } from '../app/windowManager'
|
||||
import { TITLE_BAR_HEIGHT, defaultWinOptions, isLinux } from '../config'
|
||||
import { TITLE_BAR_HEIGHT, defaultWinOptions, isLinux, isOsx } from '../config'
|
||||
import { isDirectory, isMarkdownFile, normalizeAndResolvePath } from '../filesystem'
|
||||
import { loadMarkdownFile } from '../filesystem/markdown'
|
||||
import { ensureWindowPosition } from './utils'
|
||||
|
||||
class EditorWindow extends EventEmitter {
|
||||
class EditorWindow extends BaseWindow {
|
||||
|
||||
/**
|
||||
* @param {Accessor} accessor The application accessor for application instances.
|
||||
*/
|
||||
constructor (accessor) {
|
||||
super()
|
||||
|
||||
this._accessor = accessor
|
||||
|
||||
this.id = null
|
||||
this.browserWindow = null
|
||||
super(accessor)
|
||||
this.type = WindowType.EDITOR
|
||||
this.quitting = false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,11 +47,11 @@ class EditorWindow extends EventEmitter {
|
||||
|
||||
// Enable native or custom/frameless window and titlebar
|
||||
const { titleBarStyle } = preferences.getAll()
|
||||
if (titleBarStyle === 'custom') {
|
||||
winOptions.titleBarStyle = ''
|
||||
} else if (titleBarStyle === 'native') {
|
||||
winOptions.frame = true
|
||||
winOptions.titleBarStyle = ''
|
||||
if (!isOsx) {
|
||||
winOptions.titleBarStyle = 'default'
|
||||
if (titleBarStyle === 'native') {
|
||||
winOptions.frame = true
|
||||
}
|
||||
}
|
||||
|
||||
let win = this.browserWindow = new BrowserWindow(winOptions)
|
||||
@ -160,16 +154,6 @@ class EditorWindow extends EventEmitter {
|
||||
browserWindow.webContents.send('AGANI::open-project', pathname)
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.quitting = true
|
||||
this.emit('bye')
|
||||
|
||||
this.removeAllListeners()
|
||||
this.browserWindow.destroy()
|
||||
this.browserWindow = null
|
||||
this.id = null
|
||||
}
|
||||
|
||||
// --- private ---------------------------------
|
||||
|
||||
// Only called once during window bootstrapping.
|
||||
@ -213,30 +197,6 @@ class EditorWindow extends EventEmitter {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_buildUrlWithSettings (windowId, env, userPreference) {
|
||||
// NOTE: Only send absolutely necessary values. Theme and titlebar settings
|
||||
// are sended because we delay load the preferences.
|
||||
const { debug, paths } = env
|
||||
const { codeFontFamily, codeFontSize, theme, titleBarStyle } = userPreference.getAll()
|
||||
|
||||
const baseUrl = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9091`
|
||||
: `file://${__dirname}/index.html`
|
||||
|
||||
const url = new URL(baseUrl)
|
||||
url.searchParams.set('udp', paths.userDataPath)
|
||||
url.searchParams.set('debug', debug ? '1' : '0')
|
||||
url.searchParams.set('wid', windowId)
|
||||
|
||||
// Settings
|
||||
url.searchParams.set('cff', codeFontFamily)
|
||||
url.searchParams.set('cfs', codeFontSize)
|
||||
url.searchParams.set('theme', theme)
|
||||
url.searchParams.set('tbs', titleBarStyle)
|
||||
|
||||
return url.toString()
|
||||
}
|
||||
}
|
||||
|
||||
export default EditorWindow
|
||||
|
84
src/main/windows/setting.js
Normal file
@ -0,0 +1,84 @@
|
||||
import path from 'path'
|
||||
import BaseWindow from './base'
|
||||
import { BrowserWindow, ipcMain } from 'electron'
|
||||
import { WindowType } from '../app/windowManager'
|
||||
import { TITLE_BAR_HEIGHT, defaultPreferenceWinOptions, isLinux, isOsx } from '../config'
|
||||
|
||||
|
||||
class SettingWindow extends BaseWindow {
|
||||
|
||||
/**
|
||||
* @param {Accessor} accessor The application accessor for application instances.
|
||||
*/
|
||||
constructor (accessor) {
|
||||
super(accessor)
|
||||
this.type = WindowType.SETTING
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new setting window.
|
||||
*
|
||||
* @param {*} [options] BrowserWindow options.
|
||||
*/
|
||||
createWindow (options = {}) {
|
||||
const { menu: appMenu, env, preferences } = this._accessor
|
||||
const winOptions = Object.assign({}, defaultPreferenceWinOptions, options)
|
||||
if (isLinux) {
|
||||
winOptions.icon = path.join(__static, 'logo-96px.png')
|
||||
}
|
||||
|
||||
// Enable native or custom/frameless window and titlebar
|
||||
const { titleBarStyle } = preferences.getAll()
|
||||
if (!isOsx) {
|
||||
winOptions.titleBarStyle = 'default'
|
||||
if (titleBarStyle === 'native') {
|
||||
winOptions.frame = true
|
||||
}
|
||||
}
|
||||
|
||||
let win = this.browserWindow = new BrowserWindow(winOptions)
|
||||
this.id = win.id
|
||||
|
||||
// Create a menu for the current window
|
||||
appMenu.addSettingMenu(win)
|
||||
|
||||
win.once('ready-to-show', async () => {
|
||||
win.show()
|
||||
|
||||
this.emit('window-ready-to-show')
|
||||
})
|
||||
|
||||
win.on('focus', () => {
|
||||
this.emit('window-focus')
|
||||
win.webContents.send('AGANI::window-active-status', { status: true })
|
||||
})
|
||||
|
||||
// Lost focus
|
||||
win.on('blur', () => {
|
||||
this.emit('window-blur')
|
||||
win.webContents.send('AGANI::window-active-status', { status: false })
|
||||
})
|
||||
|
||||
win.on('close', event => {
|
||||
this.emit('window-close')
|
||||
|
||||
event.preventDefault()
|
||||
ipcMain.emit('window-close-by-id', win.id)
|
||||
})
|
||||
|
||||
// The window is now destroyed.
|
||||
win.on('closed', () => {
|
||||
this.emit('window-closed')
|
||||
|
||||
// Free window reference
|
||||
win = null
|
||||
})
|
||||
|
||||
win.loadURL(this._buildUrlWithSettings(this.id, env, preferences))
|
||||
win.setSheetOffset(TITLE_BAR_HEIGHT)
|
||||
|
||||
return win
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingWindow
|
@ -157,11 +157,14 @@ export const CURSOR_DNA = getLongUniqueId()
|
||||
|
||||
export const DEFAULT_TURNDOWN_CONFIG = {
|
||||
headingStyle: 'atx', // setext or atx
|
||||
hr: '---',
|
||||
bulletListMarker: '-', // -, +, or *
|
||||
codeBlockStyle: 'fenced', // fenced or indented
|
||||
fence: '```', // ``` or ~~~
|
||||
emDelimiter: '*', // _ or *
|
||||
strongDelimiter: '**', // ** or __
|
||||
linkStyle: 'inlined',
|
||||
linkReferenceStyle: 'full',
|
||||
blankReplacement (content, node, options) {
|
||||
if (node && node.classList.contains('ag-soft-line-break')) {
|
||||
return LINE_BREAK
|
||||
@ -230,7 +233,7 @@ export const MUYA_DEFAULT_OPTION = {
|
||||
autoPairMarkdownSyntax: true,
|
||||
autoPairQuote: true,
|
||||
bulletListMarker: '-',
|
||||
orderListMarker: '.',
|
||||
orderListDelimiter: '.',
|
||||
tabSize: 4,
|
||||
// bullet/list marker width + listIndentation, tab or Daring Fireball Markdown (4 spaces) --> list indentation
|
||||
listIndentation: 1,
|
||||
|
@ -131,7 +131,7 @@ const inputCtrl = ContentState => {
|
||||
event.type === 'input'
|
||||
) {
|
||||
const { offset } = start
|
||||
const { autoPairBracket, autoPairMarkdownSyntax, autoPairQuote } = this
|
||||
const { autoPairBracket, autoPairMarkdownSyntax, autoPairQuote } = this.muya.options
|
||||
const inputChar = text.charAt(+offset - 1)
|
||||
const preInputChar = text.charAt(+offset - 2)
|
||||
const prePreInputChar = text.charAt(+offset - 3)
|
||||
|
@ -94,7 +94,7 @@ const paragraphCtrl = ContentState => {
|
||||
|
||||
ContentState.prototype.handleListMenu = function (paraType, insertMode) {
|
||||
const { start, end, affiliation } = this.selectionChange(this.cursor)
|
||||
const { orderListMarker, bulletListMarker, preferLooseListItem } = this
|
||||
const { orderListDelimiter, bulletListMarker, preferLooseListItem } = this.muya.options
|
||||
const [blockType, listType] = paraType.split('-')
|
||||
const isListed = affiliation.slice(0, 3).filter(b => /ul|ol/.test(b.type))
|
||||
|
||||
@ -127,7 +127,7 @@ const paragraphCtrl = ContentState => {
|
||||
|
||||
if (listType === 'order') {
|
||||
listBlock.start = listBlock.start || 1
|
||||
listBlock.children.forEach(b => (b.bulletMarkerOrDelimiter = orderListMarker))
|
||||
listBlock.children.forEach(b => (b.bulletMarkerOrDelimiter = orderListDelimiter))
|
||||
}
|
||||
if (
|
||||
(listType === 'bullet' && oldListType === 'order') ||
|
||||
|
@ -164,7 +164,7 @@ const updateCtrl = ContentState => {
|
||||
|
||||
ContentState.prototype.updateList = function (block, type, marker = '', line) {
|
||||
const cleanMarker = marker ? marker.trim() : null
|
||||
const { preferLooseListItem } = this
|
||||
const { preferLooseListItem } = this.muya.options
|
||||
const wrapperTag = type === 'order' ? 'ol' : 'ul' // `bullet` => `ul` and `order` => `ol`
|
||||
const { start, end } = this.cursor
|
||||
const startOffset = start.offset
|
||||
@ -210,7 +210,7 @@ const updateCtrl = ContentState => {
|
||||
if (type === 'order') {
|
||||
bulletMarkerOrDelimiter = (cleanMarker && cleanMarker.length >= 2) ? cleanMarker.slice(-1) : '.'
|
||||
} else {
|
||||
const { bulletListMarker } = this
|
||||
const { bulletListMarker } = this.muya.options
|
||||
bulletMarkerOrDelimiter = marker ? marker.charAt(0) : bulletListMarker
|
||||
}
|
||||
newListItemBlock.bulletMarkerOrDelimiter = bulletMarkerOrDelimiter
|
||||
@ -286,7 +286,7 @@ const updateCtrl = ContentState => {
|
||||
}
|
||||
|
||||
ContentState.prototype.updateTaskListItem = function (block, type, marker = '') {
|
||||
const { preferLooseListItem } = this
|
||||
const { preferLooseListItem } = this.muya.options
|
||||
const parent = this.getParent(block)
|
||||
const grandpa = this.getParent(parent)
|
||||
const checked = /\[x\]\s/i.test(marker) // use `i` flag to ignore upper case or lower case
|
||||
|
@ -17,8 +17,7 @@ class Muya {
|
||||
}
|
||||
constructor (container, options) {
|
||||
this.options = Object.assign({}, MUYA_DEFAULT_OPTION, options)
|
||||
const { focusMode, markdown } = this.options
|
||||
this.focusMode = focusMode
|
||||
const { markdown } = this.options
|
||||
this.markdown = markdown
|
||||
this.container = getContainer(container, this.options)
|
||||
this.eventCenter = new EventCenter()
|
||||
@ -42,7 +41,8 @@ class Muya {
|
||||
contentState.stateRender.setContainer(container.children[0])
|
||||
eventCenter.subscribe('stateChange', this.dispatchChange)
|
||||
contentState.listenForPathChange()
|
||||
const { focusMode, markdown } = this
|
||||
const { markdown } = this
|
||||
const { focusMode } = this.options
|
||||
this.setMarkdown(markdown)
|
||||
this.setFocusMode(focusMode)
|
||||
this.mutationObserver()
|
||||
@ -156,13 +156,14 @@ class Muya {
|
||||
}
|
||||
|
||||
setFocusMode (bool) {
|
||||
const { container, focusMode } = this
|
||||
const { container } = this
|
||||
const { focusMode } = this.options
|
||||
if (bool && !focusMode) {
|
||||
container.classList.add(CLASS_OR_ID['AG_FOCUS_MODE'])
|
||||
} else {
|
||||
container.classList.remove(CLASS_OR_ID['AG_FOCUS_MODE'])
|
||||
}
|
||||
this.focusMode = bool
|
||||
this.options.focusMode = bool
|
||||
}
|
||||
|
||||
setFont ({ fontSize, lineHeight }) {
|
||||
@ -170,10 +171,6 @@ class Muya {
|
||||
if (lineHeight) this.contentState.lineHeight = lineHeight
|
||||
}
|
||||
|
||||
setListItemPreference (preferLooseListItem) {
|
||||
this.contentState.preferLooseListItem = preferLooseListItem
|
||||
}
|
||||
|
||||
setTabSize (tabSize) {
|
||||
if (!tabSize || typeof tabSize !== 'number') {
|
||||
tabSize = 4
|
||||
@ -309,6 +306,19 @@ class Muya {
|
||||
if (needRender) {
|
||||
this.contentState.render()
|
||||
}
|
||||
// Set quick insert hint visibility
|
||||
const hideQuickInsertHint = options['hideQuickInsertHint']
|
||||
if (typeof hideQuickInsertHint !== 'undefined') {
|
||||
const hasClass = this.container.classList.contains('ag-show-quick-insert-hint')
|
||||
if (hideQuickInsertHint && hasClass) {
|
||||
this.container.classList.remove('ag-show-quick-insert-hint')
|
||||
} else if (!hideQuickInsertHint && !hasClass) {
|
||||
this.container.classList.add('ag-show-quick-insert-hint')
|
||||
}
|
||||
}
|
||||
if (options.bulletListMarker) {
|
||||
this.contentState.turndownConfig.bulletListMarker = options.bulletListMarker
|
||||
}
|
||||
}
|
||||
|
||||
destroy () {
|
||||
|
@ -150,7 +150,7 @@ kbd {
|
||||
}
|
||||
|
||||
.v-modal {
|
||||
background: var(--maskColor);
|
||||
background: var(--maskColor) !important;
|
||||
}
|
||||
|
||||
body>*:first-child {
|
||||
|
1
src/renderer/assets/icons/pref_editor.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M768.352529 965.280644 187.019441 965.280644c-53.431933 0-96.906074-43.474141-96.906074-96.907097L90.113367 222.426859c0-53.431933 43.474141-96.906074 96.906074-96.906074l452.16831 0c17.854647 0 32.29145 14.471596 32.29145 32.292474s-14.437827 32.293497-32.29145 32.293497l-452.16831 0c-17.790178 0-32.292474 14.500249-32.292474 32.287357l0 645.951805c0 17.819854 14.502295 32.287357 32.292474 32.287357l581.367881 0c17.819854 0 32.287357-14.46648 32.287357-32.287357L800.674679 351.620289c0-17.819854 14.437827-32.29145 32.292474-32.29145 17.85567 0 32.293497 14.471596 32.293497 32.29145l0 516.752234C865.259626 921.806503 821.785485 965.280644 768.352529 965.280644L768.352529 965.280644 768.352529 965.280644zM578.600861 448.52841c-8.280594 0-16.532535-3.1569-22.811542-9.476839-12.632715-12.632715-12.632715-33.029254 0-45.661969L876.761588 72.412217c12.632715-12.632715 33.034371-12.632715 45.667086 0 12.632715 12.633738 12.632715 33.031301 0 45.664016L601.451288 439.051571C595.167165 445.37151 586.886571 448.52841 578.600861 448.52841L578.600861 448.52841 578.600861 448.52841zM445.379697 448.52841 251.599272 448.52841c-17.819854 0-32.292474-14.471596-32.292474-32.293497 0-17.819854 14.471596-32.29145 32.292474-32.29145l193.779402 0c17.819854 0 32.292474 14.471596 32.292474 32.29145C477.671147 434.056813 463.199551 448.52841 445.379697 448.52841L445.379697 448.52841 445.379697 448.52841zM639.187751 642.306788 251.599272 642.306788c-17.819854 0-32.292474-14.436804-32.292474-32.292474s14.471596-32.292474 32.292474-32.292474l387.55778 0c17.850553 0 32.28838 14.436804 32.28838 32.292474C671.480224 627.869984 657.042397 642.306788 639.187751 642.306788L639.187751 642.306788 639.187751 642.306788zM639.187751 642.306788" /></svg>
|
After Width: | Height: | Size: 2.0 KiB |
1
src/renderer/assets/icons/pref_email.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M981.333333 255.2c-0.533333-70.133333-57.6-127.2-128-127.2H170.666667c-70.4 0-127.466667 56.8-128 127.2V768c0 70.666667 57.333333 128 128 128h682.666666c70.666667 0 128-57.333333 128-128V256.533333v-1.333333zM170.666667 213.333333h682.666666c16.8 0 31.2 9.6 38.133334 23.733334L512 502.666667 132.533333 237.066667c6.933333-14.133333 21.333333-23.733333 38.133334-23.733334z m682.666666 597.333334H170.666667c-23.466667 0-42.666667-19.2-42.666667-42.666667V337.866667l359.466667 251.733333c7.466667 5.066667 16 7.733333 24.533333 7.733333s17.066667-2.666667 24.533333-7.733333L896 337.866667V768c0 23.466667-19.2 42.666667-42.666667 42.666667z" /></svg>
|
After Width: | Height: | Size: 921 B |
1
src/renderer/assets/icons/pref_fontsize.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M0 853.333333l42.666667 21.333334 100.48-256H488.533333l115.84 256L640 853.333333 320 106.666667z m469.333333-277.333333H161.28L320 213.333333zM984.32 874.666667L1024 853.333333l-192-490.666666-192 490.666666 39.68 21.333334 64-170.666667h176zM761.386667 661.333333L832 477.653333 903.04 661.333333h-141.653333z" /></svg>
|
After Width: | Height: | Size: 590 B |
1
src/renderer/assets/icons/pref_general.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M415.378286 558.756571h-214.857143C140.379429 558.756571 91.428571 509.677714 91.428571 449.371429V237.385143C91.428571 177.078857 140.379429 128 200.539429 128h214.857142c60.16 0 109.092571 49.078857 109.092572 109.385143v211.986286c0 60.306286-48.950857 109.385143-109.110857 109.385142zM200.539429 203.172571a34.194286 34.194286 0 0 0-34.102858 34.212572v211.986286a34.194286 34.194286 0 0 0 34.102858 34.194285h214.857142a34.194286 34.194286 0 0 0 34.121143-34.194285V237.385143a34.194286 34.194286 0 0 0-34.121143-34.212572h-214.857142zM415.378286 896h-214.857143C140.379429 896 91.428571 846.939429 91.428571 786.651429v-71.789715c0-60.342857 48.950857-109.385143 109.110858-109.385143h214.857142c60.16 0 109.092571 49.060571 109.092572 109.385143v71.789715c0 60.288-48.950857 109.348571-109.110857 109.348571zM200.539429 680.649143a34.194286 34.194286 0 0 0-34.102858 34.212571v71.789715a34.194286 34.194286 0 0 0 34.102858 34.194285h214.857142a34.194286 34.194286 0 0 0 34.121143-34.194285v-71.789715a34.212571 34.212571 0 0 0-34.121143-34.212571h-214.857142zM823.442286 896h-143.232c-60.141714 0-109.110857-49.060571-109.110857-109.348571V237.385143C571.099429 177.078857 620.068571 128 680.210286 128h143.232C883.620571 128 932.571429 177.078857 932.571429 237.385143V786.651429c0 60.288-48.950857 109.348571-109.129143 109.348571z m-143.232-692.827429a34.194286 34.194286 0 0 0-34.102857 34.212572V786.651429a34.194286 34.194286 0 0 0 34.102857 34.194285h143.232a34.194286 34.194286 0 0 0 34.121143-34.194285V237.385143a34.194286 34.194286 0 0 0-34.121143-34.212572h-143.232z" /></svg>
|
After Width: | Height: | Size: 1.8 KiB |
1
src/renderer/assets/icons/pref_lineheight.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M179.42702 205.00814 102.678032 307.33876 164.076404 307.33876 164.076404 716.66124 102.678032 716.66124 179.42702 818.99186 256.173962 716.66124 194.77559 716.66124 194.77559 307.33876 256.173962 307.33876ZM102.678032 102.33062l818.644959 0 0 30.699186-818.644959 0 0-30.699186ZM102.678032 890.970194l818.644959 0 0 30.699186-818.644959 0 0-30.699186ZM683.91493 256.17345 531.800463 256.17345l-186.242751 511.653099 99.765191 0 50.167586-142.602835L716.785571 625.223714l50.16861 142.602835 103.202477 0L683.91493 256.17345zM526.202978 538.654057l67.264986-187.677427 25.296129 0 67.007113 187.677427L526.202978 538.654057z" /></svg>
|
After Width: | Height: | Size: 902 B |
1
src/renderer/assets/icons/pref_markdown.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M820.906667 913.066667c0 32.426667-25.6 58.026667-58.026667 58.026666H100.693333c-32.426667 0-58.026667-25.6-58.026666-58.026666V133.12c0-32.426667 25.6-58.026667 58.026666-58.026667h662.186667c32.426667 0 58.026667 25.6 58.026667 58.026667v779.946667z m-39.253334-740.693334c0-32.426667-25.6-58.026667-58.026666-58.026666H139.946667c-32.426667 0-58.026667 25.6-58.026667 58.026666v701.44c0 32.426667 25.6 58.026667 58.026667 58.026667h583.68c32.426667 0 58.026667-25.6 58.026666-58.026667V172.373333z m-97.28 76.8c0-10.24-8.533333-17.066667-17.066666-17.066666H215.04c-10.24 0-17.066667 8.533333-17.066667 17.066666v3.413334c0 10.24 8.533333 17.066667 17.066667 17.066666h452.266667c10.24 0 17.066667-8.533333 17.066666-17.066666v-3.413334z m0 136.533334c0-10.24-8.533333-17.066667-17.066666-17.066667H215.04c-10.24 0-17.066667 8.533333-17.066667 17.066667v3.413333c0 10.24 8.533333 17.066667 17.066667 17.066667h452.266667c10.24 0 17.066667-8.533333 17.066666-17.066667v-3.413333z m0 116.053333c0-10.24-8.533333-17.066667-17.066666-17.066667H215.04c-10.24 0-17.066667 8.533333-17.066667 17.066667v3.413333c0 10.24 8.533333 17.066667 17.066667 17.066667h452.266667c10.24 0 17.066667-8.533333 17.066666-17.066667v-3.413333z m-332.8 116.053333c0-10.24-8.533333-17.066667-17.066666-17.066666h-119.466667c-10.24 0-17.066667 8.533333-17.066667 17.066666v3.413334c0 10.24 8.533333 17.066667 17.066667 17.066666h119.466667c10.24 0 17.066667-8.533333 17.066666-17.066666v-3.413334zM882.346667 892.586667c-11.946667 0-22.186667-10.24-22.186667-22.186667V110.933333c0-11.946667 10.24-20.48 20.48-20.48 11.946667 0 22.186667 10.24 22.186667 20.48V870.4c0 11.946667-8.533333 22.186667-20.48 22.186667z" /><path d="M882.346667 957.44L836.266667 878.933333h87.04zM493.226667 868.693333h-27.306667V651.946667h40.96L568.32 836.266667l59.733333-184.32h40.96v216.746666h-13.653333l-13.653333-17.066666V667.306667v17.066666l-59.733334 184.32h-27.306666L494.933333 684.373333v184.32z" /></svg>
|
After Width: | Height: | Size: 2.2 KiB |
1
src/renderer/assets/icons/pref_theme.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="196.92px" viewBox="0 0 1040 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M717.056236 383.936299l-51.226708 0c-28.2893 0-51.226708 22.936385-51.226708 51.225685l0 128.062678c0 28.2893 22.937408 51.225685 51.226708 51.225685l51.226708 0c28.2893 0 51.225685-22.936385 51.225685-51.225685L768.281921 435.161984C768.281921 406.872684 745.345536 383.936299 717.056236 383.936299zM717.056236 537.611308c0 14.158465-11.480472 25.612331-25.613354 25.612331-14.132882 0-25.612331-11.453866-25.612331-25.612331l0-76.835969c0-14.158465 11.480472-25.613354 25.612331-25.613354 14.133905 0 25.613354 11.453866 25.613354 25.613354L717.056236 537.611308zM1013.977739 426.580538 859.776751 165.30079c-8.888438-15.063067-22.294772-25.975605-37.57171-32.080649-32.708959-34.856879-79.187527-56.638975-130.762159-56.638975L332.862064 76.581166c-51.575656 0-98.0532 21.782096-130.761136 56.639998-15.276938 6.105045-28.683273 17.017582-37.572734 32.079626L10.327206 426.580538c-21.26021 36.069497-8.655124 82.217537 28.239158 103.028515l115.00836 64.967664 0 199.163015c0 99.024318 80.264045 153.678078 179.287339 153.678078l358.580818 0c99.024318 0 179.290409-80.266092 179.290409-179.290409L870.733291 594.575694l115.00836-64.966641C1022.63184 508.798075 1035.238972 462.650035 1013.977739 426.580538zM153.574724 536.518417l-67.058278-37.875632c-24.589025-13.907755-33.019021-44.647873-18.809391-68.684312l85.86767-145.555074L153.574724 536.518417zM646.620024 127.807874c0 56.5786-60.205197 102.45137-134.467551 102.45137-74.261331 0-134.466528-45.873794-134.466528-102.45137L646.620024 127.807874zM819.507606 742.515071c0 84.893482-68.810179 153.677055-153.678078 153.677055L358.475418 896.192126c-84.8679 0-153.675008-68.783573-153.675008-153.677055l0-461.030142c0-76.150354 55.402821-139.361001 128.093377-151.545508 1.332345 83.883479 81.06734 151.545508 179.258687 151.545508 98.19237 0 177.926342-67.662029 179.25971-151.545508 72.690556 12.183484 128.096447 75.394131 128.096447 151.545508L819.508629 742.515071zM937.791569 498.642784l-67.058278 37.875632 0-252.111948 85.86767 145.552004C970.807521 453.995935 962.377524 484.736053 937.791569 498.642784z" /></svg>
|
After Width: | Height: | Size: 2.3 KiB |
@ -1,10 +1,21 @@
|
||||
/* Common CSS use by both light and dark themes */
|
||||
:root {
|
||||
--titleBarHeight: 32px;
|
||||
--editorAreaWidth: 700px;
|
||||
--editorAreaWidth: 750px;
|
||||
|
||||
/*editor*/
|
||||
/*Theme color cluster*/
|
||||
--themeColor: rgba(33, 181, 111, 1);
|
||||
--themeColor90: rgba(33, 181, 111, .9);
|
||||
--themeColor80: rgba(33, 181, 111, .8);
|
||||
--themeColor70: rgba(33, 181, 111, .7);
|
||||
--themeColor60: rgba(33, 181, 111, .6);
|
||||
--themeColor50: rgba(33, 181, 111, .5);
|
||||
--themeColor40: rgba(33, 181, 111, .4);
|
||||
--themeColor30: rgba(33, 181, 111, .3);
|
||||
--themeColor20: rgba(33, 181, 111, .2);
|
||||
--themeColor10: rgba(33, 181, 111, .1);
|
||||
|
||||
--highlightColor: rgba(33, 181, 111, .4);
|
||||
--selectionColor: rgba(0, 0, 0, .1);
|
||||
--editorColor: rgba(0, 0, 0, .7);
|
||||
@ -84,10 +95,6 @@ body {
|
||||
border: solid 1px var(--floatBorderColor);
|
||||
}
|
||||
|
||||
.el-tooltip__popper.is-dark .popper__arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-slider__button {
|
||||
border-color: var(--themeColor);
|
||||
}
|
||||
@ -96,7 +103,7 @@ body {
|
||||
background-color: var(--themeColor);
|
||||
}
|
||||
|
||||
.ag-dialog-table {
|
||||
.el-dialog.ag-dialog-table {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px 0 var(--floatBorderColor);
|
||||
border: solid 1px var(--floatBorderColor);
|
||||
@ -107,3 +114,9 @@ body {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.ag-underdevelop {
|
||||
opacity: .3;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
@ -1,6 +1,16 @@
|
||||
:root {
|
||||
/*editor*/
|
||||
--themeColor: #409eff;
|
||||
--themeColor90: rgba(64, 158, 255, .9);
|
||||
--themeColor80: rgba(64, 158, 255, .8);
|
||||
--themeColor70: rgba(64, 158, 255, .7);
|
||||
--themeColor60: rgba(64, 158, 255, .6);
|
||||
--themeColor50: rgba(64, 158, 255, .5);
|
||||
--themeColor40: rgba(64, 158, 255, .4);
|
||||
--themeColor30: rgba(64, 158, 255, .3);
|
||||
--themeColor20: rgba(64, 158, 255, .2);
|
||||
--themeColor10: rgba(64, 158, 255, .1);
|
||||
|
||||
--highlightColor: rgba(102, 177, 255, .6);
|
||||
--selectionColor: rgba(102, 177, 255, .3);
|
||||
--editorColor: rgba(255, 255, 255, .7);
|
||||
|
@ -1,5 +1,15 @@
|
||||
:root {
|
||||
--themeColor: rgb(104, 134, 170);
|
||||
--themeColor90: rgba(104, 134, 170, .9);
|
||||
--themeColor80: rgba(104, 134, 170, .8);
|
||||
--themeColor70: rgba(104, 134, 170, .7);
|
||||
--themeColor60: rgba(104, 134, 170, .6);
|
||||
--themeColor50: rgba(104, 134, 170, .5);
|
||||
--themeColor40: rgba(104, 134, 170, .4);
|
||||
--themeColor30: rgba(104, 134, 170, .3);
|
||||
--themeColor20: rgba(104, 134, 170, .2);
|
||||
--themeColor10: rgba(104, 134, 170, .1);
|
||||
|
||||
--highlightColor: rgba(104, 134, 170, .4);
|
||||
--selectionColor: rgba(0, 0, 0, .1);
|
||||
--editorColor: rgba(43, 48, 50, .7);
|
||||
|
@ -1,6 +1,16 @@
|
||||
:root {
|
||||
/*editor*/
|
||||
--themeColor: #f48237;
|
||||
--themeColor90: rgba(244, 130, 55, .9);
|
||||
--themeColor80: rgba(244, 130, 55, .8);
|
||||
--themeColor70: rgba(244, 130, 55, .7);
|
||||
--themeColor60: rgba(244, 130, 55, .6);
|
||||
--themeColor50: rgba(244, 130, 55, .5);
|
||||
--themeColor40: rgba(244, 130, 55, .4);
|
||||
--themeColor30: rgba(244, 130, 55, .3);
|
||||
--themeColor20: rgba(244, 130, 55, .2);
|
||||
--themeColor10: rgba(244, 130, 55, .1);
|
||||
|
||||
--highlightColor: rgba(244, 130, 55, .4);
|
||||
--selectionColor: rgba(255, 255, 255, .2);
|
||||
--editorColor: rgba(171, 178, 191, .8);
|
||||
|
@ -1,6 +1,16 @@
|
||||
:root {
|
||||
/*editor*/
|
||||
--themeColor: #e2c08d;
|
||||
--themeColor: rgba(226, 192, 141, 1);
|
||||
--themeColor90: rgba(226, 192, 141, .9);
|
||||
--themeColor80: rgba(226, 192, 141, .8);
|
||||
--themeColor70: rgba(226, 192, 141, .7);
|
||||
--themeColor60: rgba(226, 192, 141, .6);
|
||||
--themeColor50: rgba(226, 192, 141, .5);
|
||||
--themeColor40: rgba(226, 192, 141, .4);
|
||||
--themeColor30: rgba(226, 192, 141, .3);
|
||||
--themeColor20: rgba(226, 192, 141, .2);
|
||||
--themeColor10: rgba(226, 192, 141, .1);
|
||||
|
||||
--highlightColor: #ffffff10;
|
||||
--selectionColor: #67769660;
|
||||
--editorColor: #9da5b4;
|
||||
|
@ -1,5 +1,15 @@
|
||||
:root {
|
||||
--themeColor: rgb(12, 139, 186);
|
||||
--themeColor90: rgba(12, 139, 186, .9);
|
||||
--themeColor80: rgba(12, 139, 186, .8);
|
||||
--themeColor70: rgba(12, 139, 186, .7);
|
||||
--themeColor60: rgba(12, 139, 186, .6);
|
||||
--themeColor50: rgba(12, 139, 186, .5);
|
||||
--themeColor40: rgba(12, 139, 186, .4);
|
||||
--themeColor30: rgba(12, 139, 186, .3);
|
||||
--themeColor20: rgba(12, 139, 186, .2);
|
||||
--themeColor10: rgba(12, 139, 186, .1);
|
||||
|
||||
--highlightColor: rgba(12, 139, 186, .4);
|
||||
--selectionColor: rgba(0, 0, 0, .1);
|
||||
--editorColor: rgba(101, 101, 101, .7);
|
||||
|
9
src/renderer/bootstrap.js
vendored
@ -1,7 +1,7 @@
|
||||
import path from 'path'
|
||||
import { crashReporter, ipcRenderer } from 'electron'
|
||||
import log from 'electron-log'
|
||||
import EnvPaths from "common/envPaths";
|
||||
import EnvPaths from 'common/envPaths'
|
||||
|
||||
let exceptionLogger = s => console.error(s)
|
||||
|
||||
@ -25,7 +25,9 @@ const parseUrlArgs = () => {
|
||||
const titleBarStyle = params.get('tbs')
|
||||
const userDataPath = params.get('udp')
|
||||
const windowId = params.get('wid')
|
||||
const type = params.get('type')
|
||||
return {
|
||||
type,
|
||||
debug,
|
||||
userDataPath,
|
||||
windowId,
|
||||
@ -62,13 +64,14 @@ const bootstrapRenderer = () => {
|
||||
ipcRenderer.send('AGANI::handle-renderer-error', copy)
|
||||
})
|
||||
|
||||
const { debug, initialState, userDataPath, windowId } = parseUrlArgs()
|
||||
const { debug, initialState, userDataPath, windowId, type } = parseUrlArgs()
|
||||
const marktext = {
|
||||
initialState,
|
||||
env: {
|
||||
debug,
|
||||
paths: new EnvPaths(userDataPath),
|
||||
windowId
|
||||
windowId,
|
||||
type
|
||||
}
|
||||
}
|
||||
global.marktext = marktext
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div
|
||||
class="editor-wrapper"
|
||||
:class="[{ 'typewriter': typewriter, 'focus': focus, 'source': sourceCode }]"
|
||||
:style="{ 'lineHeight': lineHeight, 'fontSize': fontSize,
|
||||
:style="{ 'lineHeight': lineHeight, 'fontSize': `${fontSize}px`,
|
||||
'font-family': editorFontFamily ? `${editorFontFamily}, ${defaultFontFamily}` : `${defaultFontFamily}` }"
|
||||
:dir="textDirection"
|
||||
>
|
||||
@ -86,6 +86,7 @@
|
||||
import bus from '../../bus'
|
||||
import Search from '../search.vue'
|
||||
import { animatedScrollTo } from '../../util'
|
||||
import { addCommonStyle } from '../../util/theme'
|
||||
import { showContextMenu } from '../../contextMenu/editor'
|
||||
import Printer from '@/services/printService'
|
||||
import { DEFAULT_EDITOR_FONT_FAMILY } from '@/config'
|
||||
@ -120,10 +121,13 @@
|
||||
'autoPairMarkdownSyntax': state => state.preferences.autoPairMarkdownSyntax,
|
||||
'autoPairQuote': state => state.preferences.autoPairQuote,
|
||||
'bulletListMarker': state => state.preferences.bulletListMarker,
|
||||
'orderListDelimiter': state => state.preferences.orderListDelimiter,
|
||||
'tabSize': state => state.preferences.tabSize,
|
||||
'listIndentation': state => state.preferences.listIndentation,
|
||||
'lineHeight': state => state.preferences.lineHeight,
|
||||
'fontSize': state => state.preferences.fontSize,
|
||||
'codeFontSize': state => state.preferences.codeFontSize,
|
||||
'codeFontFamily': state => state.preferences.codeFontFamily,
|
||||
'lightColor': state => state.preferences.lightColor,
|
||||
'darkColor': state => state.preferences.darkColor,
|
||||
'editorFontFamily': state => state.preferences.editorFontFamily,
|
||||
@ -175,7 +179,9 @@
|
||||
preferLooseListItem: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setListItemPreference(value)
|
||||
editor.setOptions({
|
||||
preferLooseListItem: value
|
||||
})
|
||||
}
|
||||
},
|
||||
tabSize: function (value, oldValue) {
|
||||
@ -205,6 +211,58 @@
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setListIndentation(value)
|
||||
}
|
||||
},
|
||||
hideQuickInsertHint: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setOptions({ hideQuickInsertHint: value })
|
||||
}
|
||||
},
|
||||
autoPairBracket: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setOptions({ autoPairBracket: value })
|
||||
}
|
||||
},
|
||||
autoPairMarkdownSyntax: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setOptions({ autoPairMarkdownSyntax: value })
|
||||
}
|
||||
},
|
||||
autoPairQuote: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setOptions({ autoPairQuote: value })
|
||||
}
|
||||
},
|
||||
bulletListMarker: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setOptions({ bulletListMarker: value })
|
||||
}
|
||||
},
|
||||
orderListDelimiter: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setOptions({ orderListDelimiter: value })
|
||||
}
|
||||
},
|
||||
codeFontSize: function (value, oldValue) {
|
||||
if (value !== oldValue) {
|
||||
addCommonStyle({
|
||||
codeFontSize: value,
|
||||
codeFontFamily: this.codeFontFamily
|
||||
})
|
||||
}
|
||||
},
|
||||
codeFontFamily: function (value, oldValue) {
|
||||
if (value !== oldValue) {
|
||||
addCommonStyle({
|
||||
codeFontSize: this.codeFontSize,
|
||||
codeFontFamily: value
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -220,6 +278,7 @@
|
||||
autoPairMarkdownSyntax,
|
||||
autoPairQuote,
|
||||
bulletListMarker,
|
||||
orderListDelimiter,
|
||||
tabSize,
|
||||
listIndentation,
|
||||
hideQuickInsertHint,
|
||||
@ -243,6 +302,7 @@
|
||||
autoPairMarkdownSyntax,
|
||||
autoPairQuote,
|
||||
bulletListMarker,
|
||||
orderListDelimiter,
|
||||
tabSize,
|
||||
listIndentation,
|
||||
hideQuickInsertHint
|
||||
@ -607,25 +667,11 @@
|
||||
& .el-button {
|
||||
width: 70px;
|
||||
}
|
||||
& .el-button:focus,
|
||||
& .el-button:hover {
|
||||
& .el-button:focus {
|
||||
color: var(--themeColor);
|
||||
border-color: var(--highlightColor);
|
||||
background-color: var(--selectionColor);
|
||||
}
|
||||
& .el-button--primary {
|
||||
color: #fff;
|
||||
background: var(--themeColor);
|
||||
border-color: var(--highlightColor);
|
||||
|
||||
}
|
||||
& .el-input-number.is-controls-right .el-input__inner {
|
||||
background: var(--itemBgColor);
|
||||
color: var(--editorColor);
|
||||
}
|
||||
& .el-input-number.is-controls-right .el-input__inner:focus {
|
||||
border-color: var(--themeColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
.editor-wrapper.source {
|
||||
|
@ -1,136 +0,0 @@
|
||||
<template>
|
||||
<div class="font">
|
||||
<el-dialog
|
||||
:visible.sync="showFontSetting"
|
||||
:show-close="false"
|
||||
:modal="false"
|
||||
custom-class="ag-dialog-table"
|
||||
width="400px"
|
||||
>
|
||||
<div slot="title">
|
||||
<el-color-picker
|
||||
v-model="tempColor"
|
||||
size="small"
|
||||
@active-change="colorChange"
|
||||
></el-color-picker>
|
||||
<svg class="icon" aria-hidden="true" :style="{ 'color': tempColor }">
|
||||
<use xlink:href="#icon-font"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-fontsize"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<el-slider v-model="tempSize" :format-tooltip="formatSize" :min="12" :max="30" :step="1"
|
||||
@change="sizeChange"
|
||||
></el-slider>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="label">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-lineheight"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<el-slider v-model="tempHeight" :format-tooltip="formatHeight" :min="1" :max="3" :step="0.1"
|
||||
@change="heightChange"
|
||||
></el-slider>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import bus from '../bus'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
showFontSetting: false,
|
||||
tempSize: 16,
|
||||
tempColor: '',
|
||||
tempHeight: 1.6
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
'fontSize': state => state.preferences.fontSize,
|
||||
'lightColor': state => state.preferences.lightColor,
|
||||
'darkColor': state => state.preferences.darkColor,
|
||||
'lineHeight': state => state.preferences.lineHeight
|
||||
}),
|
||||
defaultSize () {
|
||||
return parseInt(this.fontSize, 10)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$nextTick(() => {
|
||||
bus.$on('font-setting', this.handleFontSetting)
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
bus.$off('font-setting', this.handleFontSetting)
|
||||
},
|
||||
methods: {
|
||||
handleFontSetting () {
|
||||
this.showFontSetting = true
|
||||
const { darkColor, lightColor, theme, lineHeight } = this
|
||||
this.tempSize = this.defaultSize
|
||||
this.tempColor = theme === 'dark' ? darkColor : lightColor
|
||||
this.tempHeight = +lineHeight
|
||||
},
|
||||
formatSize (val) {
|
||||
return `${val} px`
|
||||
},
|
||||
formatHeight (val) {
|
||||
return `${val}`
|
||||
},
|
||||
colorChange (color) {
|
||||
const COLOR_KEY = this.theme === 'dark' ? 'darkColor' : 'lightColor'
|
||||
this.handleChange(COLOR_KEY)(color)
|
||||
},
|
||||
sizeChange (size) {
|
||||
this.handleChange('fontSize')(size)
|
||||
},
|
||||
heightChange (height) {
|
||||
this.handleChange('lineHeight')(height)
|
||||
},
|
||||
handleChange (type) {
|
||||
return (value) => {
|
||||
if (!value) return
|
||||
if (type === 'fontSize') value = value + 'px'
|
||||
this.$store.dispatch('CHANGE_FONT', { type, value })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.font .el-color-picker__trigger {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
opacity: 0;
|
||||
}
|
||||
.font .el-dialog__header {
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.font svg.icon {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
.font .row .label {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
.font .row .el-slider {
|
||||
width: 300px;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
@ -6,7 +6,7 @@
|
||||
></div>
|
||||
<div
|
||||
class="title-bar"
|
||||
:class="[{ 'active': active }, { 'tabs-visible': showTabBar }, { 'frameless': titleBarStyle === 'custom' }, { 'isOsx': platform === 'darwin' }]"
|
||||
:class="[{ 'active': active }, { 'tabs-visible': showTabBar }, { 'frameless': titleBarStyle === 'custom' }, { 'isOsx': isOsx }]"
|
||||
>
|
||||
<div class="title">
|
||||
<span v-if="!filename">Mark Text</span>
|
||||
@ -30,9 +30,9 @@
|
||||
<span class="save-dot" :class="{'show': !isSaved}"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div :class="titleBarStyle === 'custom' ? 'left-toolbar title-no-drag' : 'right-toolbar'">
|
||||
<div :class="showCustomTitleBar ? 'left-toolbar title-no-drag' : 'right-toolbar'">
|
||||
<div
|
||||
v-if="titleBarStyle === 'custom'"
|
||||
v-if="showCustomTitleBar"
|
||||
class="frameless-titlebar-menu title-no-drag"
|
||||
@click.stop="handleMenuClick"
|
||||
>
|
||||
@ -66,7 +66,7 @@
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div
|
||||
v-if="titleBarStyle === 'custom' && !isFullScreen"
|
||||
v-if="titleBarStyle === 'custom' && !isFullScreen && !isOsx"
|
||||
class="right-toolbar"
|
||||
:class="[{ 'title-no-drag': titleBarStyle === 'custom' }]"
|
||||
>
|
||||
@ -102,9 +102,11 @@
|
||||
import { mapState } from 'vuex'
|
||||
import { minimizePath, restorePath, maximizePath, closePath } from '../assets/window-controls.js'
|
||||
import { PATH_SEPARATOR } from '../config'
|
||||
import { isOsx } from '@/util'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
this.isOsx = isOsx
|
||||
this.HASH = {
|
||||
'word': {
|
||||
short: 'W',
|
||||
@ -157,6 +159,9 @@
|
||||
if (!this.pathname) return []
|
||||
const pathnameToken = this.pathname.split(PATH_SEPARATOR).filter(i => i)
|
||||
return pathnameToken.slice(0, pathnameToken.length - 1).slice(-3)
|
||||
},
|
||||
showCustomTitleBar () {
|
||||
return this.titleBarStyle === 'custom' && !this.isOsx
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -324,6 +329,9 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
& .item {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.word-count {
|
||||
@ -389,11 +397,10 @@
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
& .front {
|
||||
color: var(--editorColor50);
|
||||
opacity: .7;
|
||||
}
|
||||
& .text {
|
||||
margin-left: 10px;
|
||||
color: var(--editorColor30);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -92,7 +92,7 @@
|
||||
color: #E6A23C;
|
||||
}
|
||||
.el-upload-dragger {
|
||||
background: var(--itemBgColor);
|
||||
background: var(--itemBgColor) !important;
|
||||
& .el-upload__text {
|
||||
color: var(--sideBarColor);
|
||||
& em {
|
||||
|
@ -2,10 +2,10 @@ import Vue from 'vue'
|
||||
import VueElectron from 'vue-electron'
|
||||
import axios from 'axios'
|
||||
import sourceMapSupport from 'source-map-support'
|
||||
import bootstrapRenderer from './bootstrap'
|
||||
import VueRouter from 'vue-router'
|
||||
import lang from 'element-ui/lib/locale/lang/en'
|
||||
import locale from 'element-ui/lib/locale'
|
||||
import bootstrapRenderer from './bootstrap'
|
||||
import App from './app'
|
||||
import store from './store'
|
||||
import './assets/symbolIcon'
|
||||
import {
|
||||
@ -20,9 +20,16 @@ import {
|
||||
ColorPicker,
|
||||
Col,
|
||||
Row,
|
||||
Tree
|
||||
Tree,
|
||||
Autocomplete,
|
||||
Switch,
|
||||
Select,
|
||||
Option,
|
||||
Radio
|
||||
} from 'element-ui'
|
||||
import services from './services'
|
||||
import routes from './router'
|
||||
import { addElementStyle } from '@/util/theme'
|
||||
|
||||
import './assets/styles/index.css'
|
||||
import './assets/styles/printService.css'
|
||||
@ -30,6 +37,9 @@ import './assets/styles/printService.css'
|
||||
// -----------------------------------------------
|
||||
|
||||
// Decode source map in production - must be registered first
|
||||
|
||||
addElementStyle()
|
||||
|
||||
sourceMapSupport.install({
|
||||
environment: 'node',
|
||||
handleUncaughtExceptions: false,
|
||||
@ -57,6 +67,13 @@ Vue.use(ColorPicker)
|
||||
Vue.use(Col)
|
||||
Vue.use(Row)
|
||||
Vue.use(Tree)
|
||||
Vue.use(Autocomplete)
|
||||
Vue.use(Switch)
|
||||
Vue.use(Select)
|
||||
Vue.use(Option)
|
||||
Vue.use(Radio)
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
Vue.use(VueElectron)
|
||||
Vue.http = Vue.prototype.$http = axios
|
||||
@ -66,9 +83,13 @@ services.forEach(s => {
|
||||
Vue.prototype['$' + s.name] = s[s.name]
|
||||
})
|
||||
|
||||
const router = new VueRouter({
|
||||
routes: routes(global.marktext.env.type)
|
||||
})
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
components: { App },
|
||||
store,
|
||||
template: '<App/>'
|
||||
router,
|
||||
template: '<router-view class="view"></router-view>'
|
||||
}).$mount('#app')
|
||||
|
@ -29,7 +29,6 @@
|
||||
<aidou></aidou>
|
||||
<upload-image></upload-image>
|
||||
<about-dialog></about-dialog>
|
||||
<font></font>
|
||||
<rename></rename>
|
||||
<tweet></tweet>
|
||||
<import-modal></import-modal>
|
||||
@ -46,7 +45,6 @@
|
||||
import Aidou from '@/components/aidou/aidou'
|
||||
import UploadImage from '@/components/uploadImage'
|
||||
import AboutDialog from '@/components/about'
|
||||
import Font from '@/components/font'
|
||||
import Rename from '@/components/rename'
|
||||
import Tweet from '@/components/tweet'
|
||||
import ImportModal from '@/components/import'
|
||||
@ -64,7 +62,6 @@
|
||||
SideBar,
|
||||
UploadImage,
|
||||
AboutDialog,
|
||||
Font,
|
||||
Rename,
|
||||
Tweet,
|
||||
ImportModal
|
95
src/renderer/pages/preference.vue
Normal file
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div class="pref-container">
|
||||
<title-bar v-if="showCustomTitleBar"></title-bar>
|
||||
<side-bar></side-bar>
|
||||
<div
|
||||
class="pref-content"
|
||||
:class="{ 'frameless': titleBarStyle === 'custom' || isOsx }"
|
||||
>
|
||||
<div class="title-bar" v-if="!showCustomTitleBar"></div>
|
||||
<router-view class="pref-setting"></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import TitleBar from '@/prefComponents/common/titlebar'
|
||||
import SideBar from '@/prefComponents/sideBar'
|
||||
import { addThemeStyle } from '@/util/theme'
|
||||
import { DEFAULT_STYLE } from '@/config'
|
||||
import { isOsx } from '@/util'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
this.isOsx = isOsx
|
||||
return {}
|
||||
},
|
||||
components: {
|
||||
TitleBar,
|
||||
SideBar
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
'theme': state => state.preferences.theme,
|
||||
'titleBarStyle': state => state.preferences.titleBarStyle
|
||||
}),
|
||||
showCustomTitleBar () {
|
||||
return this.titleBarStyle === 'custom' && !this.isOsx
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
theme: function (value, oldValue) {
|
||||
if (value !== oldValue) {
|
||||
addThemeStyle(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$nextTick(() => {
|
||||
const state = global.marktext.initialState || DEFAULT_STYLE
|
||||
addThemeStyle(state.theme)
|
||||
|
||||
this.$store.dispatch('ASK_FOR_USER_PREFERENCE')
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pref-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
background: var(--editorBgColor);
|
||||
& .pref-content {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& .title-bar {
|
||||
width: 100%;
|
||||
height: var(--titleBarHeight);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
& .pref-setting {
|
||||
padding: 50px 20px;
|
||||
padding-top: var(--titleBarHeight);
|
||||
flex: 1;
|
||||
height: calc(100vh - var(--titleBarHeight));
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
& .pref-content.frameless .pref-setting {
|
||||
/* Move the scrollbar below the titlebar */
|
||||
margin-top: var(--titleBarHeight);
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
93
src/renderer/prefComponents/common/bool/index.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<section class="pref-switch-item" :class="{'ag-underdevelop': disable}">
|
||||
<div class="description">
|
||||
<span>{{description}}</span>
|
||||
<i class="el-icon-info" v-if="more"
|
||||
@click="handleMoreClick"
|
||||
></i>
|
||||
</div>
|
||||
<el-switch
|
||||
style="display: block"
|
||||
v-model="status"
|
||||
@change="handleSwitchChange"
|
||||
:active-text="status ? 'On': 'Off'">
|
||||
</el-switch>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { shell } from 'electron'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
status: this.bool
|
||||
}
|
||||
},
|
||||
props: {
|
||||
description: String,
|
||||
bool: Boolean,
|
||||
onChange: Function,
|
||||
more: String,
|
||||
disable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
bool: function (value, oldValue) {
|
||||
if (value !== oldValue) {
|
||||
this.status = value
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMoreClick () {
|
||||
if (typeof this.more === 'string') {
|
||||
shell.openExternal(this.more)
|
||||
}
|
||||
},
|
||||
handleSwitchChange (value) {
|
||||
this.onChange(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pref-switch-item {
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
margin: 20px 0;
|
||||
color: var(--editorColor);
|
||||
}
|
||||
.pref-switch-item .description {
|
||||
margin-bottom: 10px;
|
||||
& i {
|
||||
cursor: pointer;
|
||||
opacity: .7;
|
||||
color: var(--iconColor);
|
||||
}
|
||||
& i:hover {
|
||||
color: var(--themeColor);
|
||||
}
|
||||
}
|
||||
span.el-switch__core::after {
|
||||
top: 3px;
|
||||
left: 7px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
.el-switch .el-switch__core {
|
||||
border: 2px solid var(--iconColor);
|
||||
background: transparent;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
span.el-switch__label {
|
||||
color: var(--editorColor50);
|
||||
}
|
||||
.el-switch:not(.is-checked) .el-switch__core::after {
|
||||
background: var(--iconColor);
|
||||
}
|
||||
</style>
|
||||
|
100
src/renderer/prefComponents/common/range/index.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<section class="pref-range-item" :class="{'ag-underdevelop': disable}">
|
||||
<div class="description">
|
||||
<span>{{description}}: <span class="value">{{selectValue + (unit ? unit : '')}}</span></span>
|
||||
<i class="el-icon-info" v-if="more"
|
||||
@click="handleMoreClick"
|
||||
></i>
|
||||
</div>
|
||||
<el-slider
|
||||
v-model="selectValue"
|
||||
@change="select"
|
||||
:min="min"
|
||||
:max="max"
|
||||
:format-tooltip="value => value + (unit ? unit : '')"
|
||||
:step="step">
|
||||
</el-slider>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { shell } from 'electron'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
selectValue: this.value
|
||||
}
|
||||
},
|
||||
props: {
|
||||
description: String,
|
||||
value: String | Number,
|
||||
min: Number,
|
||||
max: Number,
|
||||
onChange: Function,
|
||||
unit: String,
|
||||
step: Number,
|
||||
more: String,
|
||||
disable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: function (value, oldValue) {
|
||||
if (value !== oldValue) {
|
||||
this.selectValue = value
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMoreClick () {
|
||||
if (typeof this.more === 'string') {
|
||||
shell.openExternal(this.more)
|
||||
}
|
||||
},
|
||||
select (value) {
|
||||
this.onChange(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pref-range-item {
|
||||
margin: 20px 0;
|
||||
font-size: 14px;
|
||||
color: var(--editorColor);
|
||||
& .el-slider {
|
||||
width: 300px;
|
||||
}
|
||||
& .el-slider__runway,
|
||||
& .el-slider__bar {
|
||||
height: 4px;
|
||||
}
|
||||
& .el-slider__button {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
& .el-slider__button-wrapper {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: -9px;
|
||||
}
|
||||
}
|
||||
.pref-select-item .description {
|
||||
margin-bottom: 10px;
|
||||
& .value {
|
||||
color: var(--editorColor80);
|
||||
}
|
||||
& i {
|
||||
cursor: pointer;
|
||||
opacity: .7;
|
||||
color: var(--iconColor);
|
||||
}
|
||||
& i:hover {
|
||||
color: var(--themeColor);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
107
src/renderer/prefComponents/common/select/index.vue
Normal file
@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<section class="pref-select-item" :class="{'ag-underdevelop': disable}">
|
||||
<div class="description">
|
||||
<span>{{description}}</span>
|
||||
<i class="el-icon-info" v-if="more"
|
||||
@click="handleMoreClick"
|
||||
></i>
|
||||
</div>
|
||||
<el-select v-model="selectValue"
|
||||
@change="select"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { shell } from 'electron'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
selectValue: this.value
|
||||
}
|
||||
},
|
||||
props: {
|
||||
description: String,
|
||||
value: String | Number,
|
||||
options: Array,
|
||||
onChange: Function,
|
||||
more: String,
|
||||
disable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: function (value, oldValue) {
|
||||
if (value !== oldValue) {
|
||||
this.selectValue = value
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMoreClick () {
|
||||
if (typeof this.more === 'string') {
|
||||
shell.openExternal(this.more)
|
||||
}
|
||||
},
|
||||
select (value) {
|
||||
this.onChange(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pref-select-item {
|
||||
margin: 20px 0;
|
||||
font-size: 14px;
|
||||
color: var(--editorColor);
|
||||
& .el-select {
|
||||
width: 180px;
|
||||
}
|
||||
& input.el-input__inner {
|
||||
height: 30px;
|
||||
background: transparent;
|
||||
color: var(--editorColor);
|
||||
border-color: var(--editorColor10);
|
||||
}
|
||||
& .el-input__icon,
|
||||
& .el-input__inner {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
.pref-select-item .description {
|
||||
margin-bottom: 10px;
|
||||
& i {
|
||||
cursor: pointer;
|
||||
opacity: .7;
|
||||
color: var(--iconColor);
|
||||
}
|
||||
& i:hover {
|
||||
color: var(--themeColor);
|
||||
}
|
||||
}
|
||||
li.el-select-dropdown__item {
|
||||
color: var(--editorColor);
|
||||
height: 30px;
|
||||
}
|
||||
li.el-select-dropdown__item.hover, li.el-select-dropdown__item:hover {
|
||||
background: var(--floatHoverColor);
|
||||
}
|
||||
div.el-select-dropdown {
|
||||
background: var(--floatBgColor);
|
||||
border-color: var(--floatBorderColor);
|
||||
& .popper__arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
12
src/renderer/prefComponents/common/separator/index.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="pref-separator"></div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.pref-separator {
|
||||
margin: 20px 0 20px 0;
|
||||
height: 2px;
|
||||
background: var(--editorColor04);
|
||||
}
|
||||
</style>
|
||||
|
86
src/renderer/prefComponents/common/titlebar.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="title-bar">
|
||||
<div class="title">
|
||||
<span>Mark Text - Settings</span>
|
||||
</div>
|
||||
<div class="frameless-titlebar-button frameless-titlebar-close" @click.stop="handleCloseClick">
|
||||
<div>
|
||||
<svg width="10" height="10">
|
||||
<path :d="windowIconClose" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { remote } from 'electron'
|
||||
import { closePath } from '../../assets/window-controls.js'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
this.windowIconClose = closePath
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
handleCloseClick () {
|
||||
remote.getCurrentWindow().close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title-bar {
|
||||
-webkit-app-region: drag;
|
||||
user-select: none;
|
||||
background: transparent;
|
||||
height: var(--titleBarHeight);
|
||||
box-sizing: border-box;
|
||||
color: var(--editorColor50);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
transition: color .4s ease-in-out;
|
||||
cursor: default;
|
||||
}
|
||||
.title {
|
||||
padding: 0 142px;
|
||||
height: 100%;
|
||||
line-height: var(--titleBarHeight);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
transition: all .25s ease-in-out;
|
||||
}
|
||||
.title:hover {
|
||||
color: var(sideBarTitleColor);
|
||||
}
|
||||
|
||||
.frameless-titlebar-button {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 46px;
|
||||
height: var(--titleBarHeight);
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
.frameless-titlebar-button > div {
|
||||
position: absolute;
|
||||
display: inline-flex;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
.frameless-titlebar-close:hover {
|
||||
background-color: rgb(228, 79, 79);
|
||||
}
|
||||
.frameless-titlebar-button svg {
|
||||
fill: #000000
|
||||
}
|
||||
.frameless-titlebar-close:hover svg {
|
||||
fill: #ffffff
|
||||
}
|
||||
</style>
|
52
src/renderer/prefComponents/editor/config.js
Normal file
@ -0,0 +1,52 @@
|
||||
export const editorFontFamilyOptions = [{
|
||||
label: 'Open Sans',
|
||||
value: 'Open Sans'
|
||||
}, {
|
||||
label: 'Clear Sans',
|
||||
value: 'Clear Sans'
|
||||
}, {
|
||||
label: 'Helvetica Neue',
|
||||
value: 'Helvetica Neue'
|
||||
}, {
|
||||
label: 'Helvetica',
|
||||
value: 'Helvetica'
|
||||
}, {
|
||||
label: 'Arial',
|
||||
value: 'Arial'
|
||||
}, {
|
||||
label: 'sans-serif',
|
||||
value: 'sans-serif'
|
||||
}]
|
||||
|
||||
export const endOfLineOptions = [{
|
||||
label: 'default',
|
||||
value: 'default'
|
||||
}, {
|
||||
label: 'Carriage return and Line feed(CRLF)',
|
||||
value: 'crlf'
|
||||
}, {
|
||||
label: 'Line feed(Lf)',
|
||||
value: 'lf'
|
||||
}]
|
||||
|
||||
export const textDirectionOptions = [{
|
||||
label: 'Left to Right',
|
||||
value: 'ltr'
|
||||
}, {
|
||||
label: 'Right to Left',
|
||||
value: 'rtl'
|
||||
}]
|
||||
|
||||
export const codeFontFamilyOptions = [{
|
||||
label: 'DejaVu Sans Mono',
|
||||
value: 'DejaVu Sans Mono'
|
||||
}, {
|
||||
label: 'Source Code Pro',
|
||||
value: 'Source Code Pro'
|
||||
}, {
|
||||
label: 'Droid Sans Mono',
|
||||
value: 'Droid Sans Mono'
|
||||
}, {
|
||||
label: 'monospace',
|
||||
value: 'monospace'
|
||||
}]
|
157
src/renderer/prefComponents/editor/index.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="pref-editor">
|
||||
<h4>Editor</h4>
|
||||
<range
|
||||
description="Font size in editor"
|
||||
:value="fontSize"
|
||||
:min="12"
|
||||
:max="32"
|
||||
unit="px"
|
||||
:step="1"
|
||||
:onChange="value => onSelectChange('fontSize', value)"
|
||||
></range>
|
||||
<cur-select
|
||||
description="Font used in editor"
|
||||
:value="editorFontFamily"
|
||||
:options="editorFontFamilyOptions"
|
||||
:onChange="value => onSelectChange('editorFontFamily', value)"
|
||||
></cur-select>
|
||||
<range
|
||||
description="Line height in editor"
|
||||
:value="lineHeight"
|
||||
:min="1.2"
|
||||
:max="2.0"
|
||||
:step="0.1"
|
||||
:onChange="value => onSelectChange('lineHeight', value)"
|
||||
></range>
|
||||
<separator></separator>
|
||||
<bool
|
||||
description="Automatically brackets when editing"
|
||||
:bool="autoPairBracket"
|
||||
:onChange="value => onSelectChange('autoPairBracket', value)"
|
||||
></bool>
|
||||
<bool
|
||||
description="Autocomplete markdown syntax"
|
||||
:bool="autoPairMarkdownSyntax"
|
||||
:onChange="value => onSelectChange('autoPairMarkdownSyntax', value)"
|
||||
></bool>
|
||||
<bool
|
||||
description="Automatic completion of quotes"
|
||||
:bool="autoPairQuote"
|
||||
:onChange="value => onSelectChange('autoPairQuote', value)"
|
||||
></bool>
|
||||
<separator></separator>
|
||||
<cur-select
|
||||
description="The default end of line character, if you select default, which will be selected according to your system intelligence"
|
||||
:value="endOfLine"
|
||||
:options="endOfLineOptions"
|
||||
:onChange="value => onSelectChange('endOfLine', value)"
|
||||
></cur-select>
|
||||
<cur-select
|
||||
description="The writing text direction"
|
||||
:value="textDirection"
|
||||
:options="textDirectionOptions"
|
||||
:onChange="value => onSelectChange('textDirection', value)"
|
||||
></cur-select>
|
||||
<separator></separator>
|
||||
<range
|
||||
description="Code block font size in editor"
|
||||
:value="codeFontSize"
|
||||
:min="12"
|
||||
:max="28"
|
||||
unit="px"
|
||||
:step="1"
|
||||
:onChange="value => onSelectChange('codeFontSize', value)"
|
||||
></range>
|
||||
<cur-select
|
||||
description="Font used in code block"
|
||||
:value="codeFontFamily"
|
||||
:options="codeFontFamilyOptions"
|
||||
:onChange="value => onSelectChange('codeFontFamily', value)"
|
||||
></cur-select>
|
||||
<separator></separator>
|
||||
<bool
|
||||
description="Hide hint for quickly creating paragraphs"
|
||||
:bool="hideQuickInsertHint"
|
||||
:onChange="value => onSelectChange('hideQuickInsertHint', value)"
|
||||
></bool>
|
||||
<separator></separator>
|
||||
<section class="image-ctrl ag-underdevelop">
|
||||
<div>The default behavior after paste or drag the image to Mark Text</div>
|
||||
<el-radio v-model="imageDropAction" label="upload">Upload image to cloud</el-radio>
|
||||
<el-radio v-model="imageDropAction" label="folder">Move image to sepcial folder</el-radio>
|
||||
<el-radio v-model="imageDropAction" label="path">Insert absolute or relative path of image</el-radio>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import Range from '../common/range'
|
||||
import CurSelect from '../common/select'
|
||||
import Bool from '../common/bool'
|
||||
import Separator from '../common/separator'
|
||||
import {
|
||||
editorFontFamilyOptions,
|
||||
endOfLineOptions,
|
||||
textDirectionOptions,
|
||||
codeFontFamilyOptions
|
||||
} from './config'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Range,
|
||||
CurSelect,
|
||||
Bool,
|
||||
Separator
|
||||
},
|
||||
data () {
|
||||
this.editorFontFamilyOptions = editorFontFamilyOptions
|
||||
this.endOfLineOptions = endOfLineOptions
|
||||
this.textDirectionOptions = textDirectionOptions
|
||||
this.codeFontFamilyOptions = codeFontFamilyOptions
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
fontSize: state => state.preferences.fontSize,
|
||||
editorFontFamily: state => state.preferences.editorFontFamily,
|
||||
lineHeight: state => state.preferences.lineHeight,
|
||||
autoPairBracket: state => state.preferences.autoPairBracket,
|
||||
autoPairMarkdownSyntax: state => state.preferences.autoPairMarkdownSyntax,
|
||||
autoPairQuote: state => state.preferences.autoPairQuote,
|
||||
endOfLine: state => state.preferences.endOfLine,
|
||||
textDirection: state => state.preferences.textDirection,
|
||||
codeFontSize: state => state.preferences.codeFontSize,
|
||||
codeFontFamily: state => state.preferences.codeFontFamily,
|
||||
hideQuickInsertHint: state => state.preferences.hideQuickInsertHint,
|
||||
imageDropAction: state => state.preferences.imageDropAction
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
onSelectChange (type, value) {
|
||||
this.$store.dispatch('SET_SINGLE_PREFERENCE', { type, value })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pref-editor {
|
||||
& h4 {
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
font-weight: 100;
|
||||
}
|
||||
& .image-ctrl {
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
margin: 20px 0;
|
||||
color: var(--editorColor);
|
||||
& label {
|
||||
display: block;
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
23
src/renderer/prefComponents/general/config.js
Normal file
@ -0,0 +1,23 @@
|
||||
export const titleBarStyleOptions = [{
|
||||
label: 'Custom',
|
||||
value: 'custom'
|
||||
}, {
|
||||
label: 'Native',
|
||||
value: 'native'
|
||||
}]
|
||||
|
||||
export const fileSortByOptions = [{
|
||||
label: 'Create time',
|
||||
value: 'created'
|
||||
}, {
|
||||
label: 'Modified time',
|
||||
value: 'modified'
|
||||
}, {
|
||||
label: 'Title',
|
||||
value: 'title'
|
||||
}]
|
||||
|
||||
export const languageOptions = [{
|
||||
label: 'English',
|
||||
value: 'en'
|
||||
}]
|
131
src/renderer/prefComponents/general/index.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="pref-general">
|
||||
<h4>General</h4>
|
||||
<bool
|
||||
description="Automatically save the content being edited"
|
||||
:bool="autoSave"
|
||||
:onChange="value => onSelectChange('autoSave', value)"
|
||||
></bool>
|
||||
<range
|
||||
description="How long do you want to save your document?"
|
||||
:value="autoSaveDelay"
|
||||
:min="3000"
|
||||
:max="10000"
|
||||
unit="ms"
|
||||
:step="100"
|
||||
:onChange="value => onSelectChange('autoSaveDelay', value)"
|
||||
:disable="true"
|
||||
></range>
|
||||
<cur-select
|
||||
v-if="!isOsx"
|
||||
description="The title bar style, frameless or not. (You need to restart Mark Text to enable it)"
|
||||
:value="titleBarStyle"
|
||||
:options="titleBarStyleOptions"
|
||||
:onChange="value => onSelectChange('titleBarStyle', value)"
|
||||
></cur-select>
|
||||
<separator></separator>
|
||||
<bool
|
||||
description="Open file in new window"
|
||||
:bool="openFilesInNewWindow"
|
||||
:onChange="value => onSelectChange('openFilesInNewWindow', value)"
|
||||
></bool>
|
||||
<bool
|
||||
description="Enable Aidou"
|
||||
:bool="aidou"
|
||||
:onChange="value => onSelectChange('aidou', value)"
|
||||
></bool>
|
||||
<separator></separator>
|
||||
<cur-select
|
||||
description="Sort files in opened folder by created time modified time and title"
|
||||
:value="fileSortBy"
|
||||
:options="fileSortByOptions"
|
||||
:onChange="value => onSelectChange('fileSortBy', value)"
|
||||
:disable="true"
|
||||
></cur-select>
|
||||
<section class="startup-ctrl ag-underdevelop">
|
||||
<div>The action after Mark Text startup, open the last edited content, open the specified folder or blank page</div>
|
||||
<el-radio v-model="startUp" label="lastState">Open the last closed folder and files</el-radio>
|
||||
<el-radio v-model="startUp" label="folder">Open the subfolder</el-radio>
|
||||
<el-button size="small">Select Folder</el-button>
|
||||
<el-radio v-model="startUp" label="blank">Open blank page</el-radio>
|
||||
</section>
|
||||
<cur-select
|
||||
description="The language Mark Text use"
|
||||
:value="language"
|
||||
:options="languageOptions"
|
||||
:onChange="value => onSelectChange('language', value)"
|
||||
:disable="true"
|
||||
></cur-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import Range from '../common/range'
|
||||
import CurSelect from '../common/select'
|
||||
import Bool from '../common/bool'
|
||||
import Separator from '../common/separator'
|
||||
import { isOsx } from '@/util'
|
||||
|
||||
import {
|
||||
titleBarStyleOptions,
|
||||
fileSortByOptions,
|
||||
languageOptions
|
||||
} from './config'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Bool,
|
||||
Range,
|
||||
CurSelect,
|
||||
Separator
|
||||
},
|
||||
data () {
|
||||
this.titleBarStyleOptions = titleBarStyleOptions
|
||||
this.fileSortByOptions = fileSortByOptions
|
||||
this.languageOptions = languageOptions
|
||||
this.isOsx = isOsx
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
autoSave: state => state.preferences.autoSave,
|
||||
autoSaveDelay: state => state.preferences.autoSaveDelay,
|
||||
titleBarStyle: state => state.preferences.titleBarStyle,
|
||||
openFilesInNewWindow: state => state.preferences.openFilesInNewWindow,
|
||||
aidou: state => state.preferences.aidou,
|
||||
fileSortBy: state => state.preferences.fileSortBy,
|
||||
startUp: state => state.preferences.startUp,
|
||||
language: state => state.preferences.language
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
onSelectChange (type, value) {
|
||||
this.$store.dispatch('SET_SINGLE_PREFERENCE', { type, value })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pref-general {
|
||||
& h4 {
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
font-weight: 100;
|
||||
}
|
||||
& .startup-ctrl {
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
margin: 20px 0;
|
||||
color: var(--editorColor);
|
||||
& .el-button--small {
|
||||
margin-left: 25px;
|
||||
}
|
||||
& label {
|
||||
display: block;
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
60
src/renderer/prefComponents/markdown/config.js
Normal file
@ -0,0 +1,60 @@
|
||||
export const bulletListMarkerOptions = [{
|
||||
label: '*',
|
||||
value: '*'
|
||||
}, {
|
||||
label: '-',
|
||||
value: '-'
|
||||
}, {
|
||||
label: '+',
|
||||
value: '+'
|
||||
}]
|
||||
|
||||
export const orderListDelimiterOptions = [{
|
||||
label: '.',
|
||||
value: '.'
|
||||
}, {
|
||||
label: ')',
|
||||
value: ')'
|
||||
}]
|
||||
|
||||
export const preferHeadingStyleOptions = [{
|
||||
label: 'ATX heading',
|
||||
value: 'atx'
|
||||
}, {
|
||||
label: 'Setext heading',
|
||||
value: 'setext'
|
||||
}]
|
||||
|
||||
export const tabSizeOptions =[{
|
||||
label: '1',
|
||||
value: 1
|
||||
}, {
|
||||
label: '2',
|
||||
value: 2
|
||||
}, {
|
||||
label: '1',
|
||||
value: 3
|
||||
}, {
|
||||
label: '4',
|
||||
value: 4
|
||||
}]
|
||||
|
||||
export const listIndentationOptions = [{
|
||||
label: 'dfm',
|
||||
value: 'dfm'
|
||||
}, {
|
||||
label: 'tab',
|
||||
value: 'tab'
|
||||
}, {
|
||||
label: '1 space',
|
||||
value: 1,
|
||||
}, {
|
||||
label: '2 spaces',
|
||||
value: 2
|
||||
}, {
|
||||
label: '3 spaces',
|
||||
value: 3
|
||||
}, {
|
||||
label: '4 spaces',
|
||||
value: 4
|
||||
}]
|
97
src/renderer/prefComponents/markdown/index.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="pref-markdown">
|
||||
<h4>markdown</h4>
|
||||
<bool
|
||||
description="Preferred loose list item"
|
||||
:bool="preferLooseListItem"
|
||||
:onChange="value => onSelectChange('preferLooseListItem', value)"
|
||||
more="https://spec.commonmark.org/0.29/#loose"
|
||||
></bool>
|
||||
<cus-select
|
||||
description="The preferred marker used in bullet list"
|
||||
:value="bulletListMarker"
|
||||
:options="bulletListMarkerOptions"
|
||||
:onChange="value => onSelectChange('bulletListMarker', value)"
|
||||
more="https://spec.commonmark.org/0.29/#bullet-list-marker"
|
||||
></cus-select>
|
||||
<cus-select
|
||||
description="The preferred dilimiter used in order list"
|
||||
:value="orderListDelimiter"
|
||||
:options="orderListDelimiterOptions"
|
||||
:onChange="value => onSelectChange('orderListDelimiter', value)"
|
||||
more="https://spec.commonmark.org/0.29/#ordered-list"
|
||||
></cus-select>
|
||||
<cus-select
|
||||
description="The preferred heading style"
|
||||
:value="preferHeadingStyle"
|
||||
:options="preferHeadingStyleOptions"
|
||||
:onChange="value => onSelectChange('preferHeadingStyle', value)"
|
||||
:disable="true"
|
||||
></cus-select>
|
||||
<cus-select
|
||||
description="The number of spaces a tab is equal to"
|
||||
:value="tabSize"
|
||||
:options="tabSizeOptions"
|
||||
:onChange="value => onSelectChange('tabSize', value)"
|
||||
></cus-select>
|
||||
<cus-select
|
||||
description="The list indentation of sub list items or paragraphs"
|
||||
:value="listIndentation"
|
||||
:options="listIndentationOptions"
|
||||
:onChange="value => onSelectChange('listIndentation', value)"
|
||||
></cus-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import Bool from '../common/bool'
|
||||
import CusSelect from '../common/select'
|
||||
import {
|
||||
bulletListMarkerOptions,
|
||||
orderListDelimiterOptions,
|
||||
preferHeadingStyleOptions,
|
||||
tabSizeOptions,
|
||||
listIndentationOptions
|
||||
} from './config'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Bool,
|
||||
CusSelect
|
||||
},
|
||||
data () {
|
||||
this.bulletListMarkerOptions = bulletListMarkerOptions
|
||||
this.orderListDelimiterOptions = orderListDelimiterOptions
|
||||
this.preferHeadingStyleOptions = preferHeadingStyleOptions
|
||||
this.tabSizeOptions = tabSizeOptions
|
||||
this.listIndentationOptions = listIndentationOptions
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
preferLooseListItem: state => state.preferences.preferLooseListItem,
|
||||
bulletListMarker: state => state.preferences.bulletListMarker,
|
||||
orderListDelimiter: state => state.preferences.orderListDelimiter,
|
||||
preferHeadingStyle: state => state.preferences.preferHeadingStyle,
|
||||
tabSize: state => state.preferences.tabSize,
|
||||
listIndentation: state => state.preferences.listIndentation
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
onSelectChange (type, value) {
|
||||
this.$store.dispatch('SET_SINGLE_PREFERENCE', { type, value })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pref-markdown {
|
||||
& h4 {
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
font-weight: 100;
|
||||
}
|
||||
}
|
||||
</style>
|
36
src/renderer/prefComponents/sideBar/config.js
Normal file
@ -0,0 +1,36 @@
|
||||
import GeneralIcon from '@/assets/icons/pref_general.svg'
|
||||
import EditorIcon from '@/assets/icons/pref_editor.svg'
|
||||
import MarkdownIcon from '@/assets/icons/pref_markdown.svg'
|
||||
import ThemeIcon from '@/assets/icons/pref_theme.svg'
|
||||
|
||||
import preferences from '../../../main/preferences/schema'
|
||||
|
||||
export const category = [{
|
||||
name: 'General',
|
||||
icon: GeneralIcon,
|
||||
path: '/preference/general'
|
||||
}, {
|
||||
name: 'Editor',
|
||||
icon: EditorIcon,
|
||||
path: '/preference/editor'
|
||||
}, {
|
||||
name: 'Markdown',
|
||||
icon: MarkdownIcon,
|
||||
path: '/preference/markdown'
|
||||
}, {
|
||||
name: 'Theme',
|
||||
icon: ThemeIcon,
|
||||
path: '/preference/theme'
|
||||
}]
|
||||
|
||||
export const searchContent = Object.keys(preferences).map(k => {
|
||||
const { description, enum: emums } = preferences[k]
|
||||
let [category, preference] = description.split('--')
|
||||
if (Array.isArray(emums)) {
|
||||
preference += ` optional values: ${emums.join(', ')}`
|
||||
}
|
||||
return {
|
||||
category,
|
||||
preference
|
||||
}
|
||||
})
|
190
src/renderer/prefComponents/sideBar/index.vue
Normal file
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div class="pref-sidebar">
|
||||
<h3 class="title">Preference</h3>
|
||||
<section class="search-wrapper">
|
||||
<el-autocomplete
|
||||
popper-class="pref-autocomplete"
|
||||
v-model="state"
|
||||
:fetch-suggestions="querySearch"
|
||||
placeholder="Search preference..."
|
||||
:trigger-on-focus="false"
|
||||
@select="handleSelect">
|
||||
<i
|
||||
class="el-icon-search el-input__icon"
|
||||
slot="suffix"
|
||||
>
|
||||
</i>
|
||||
<template slot-scope="{ item }">
|
||||
<div class="name">{{ item.category }}</div>
|
||||
<span class="addr">{{ item.preference }}</span>
|
||||
</template>
|
||||
</el-autocomplete>
|
||||
</section>
|
||||
<section class="category">
|
||||
<div v-for="c of category" :key="c.name" class="item"
|
||||
@click="handleCategoryItemClick(c)"
|
||||
:class="{active: c.name.toLowerCase() === currentCategory}"
|
||||
>
|
||||
<svg :viewBox="c.icon.viewBox">
|
||||
<use :xlink:href="c.icon.url"></use>
|
||||
</svg>
|
||||
<span>{{c.name}}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { category, searchContent } from './config'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
this.category = category
|
||||
return {
|
||||
currentCategory: 'general',
|
||||
restaurants: [],
|
||||
state: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route' (to, from) {
|
||||
if (to.name !== from.name) {
|
||||
this.currentCategory = to.name
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
querySearch(queryString, cb) {
|
||||
var restaurants = this.restaurants
|
||||
var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
|
||||
// call callback return this results
|
||||
cb(results)
|
||||
},
|
||||
createFilter(queryString) {
|
||||
return (restaurant) => {
|
||||
return (restaurant.preference.toLowerCase().indexOf(queryString.toLowerCase()) >= 0) ||
|
||||
(restaurant.category.toLowerCase().indexOf(queryString.toLowerCase()) >= 0)
|
||||
}
|
||||
},
|
||||
loadAll() {
|
||||
return searchContent
|
||||
},
|
||||
handleSelect(item) {
|
||||
this.$router.push({
|
||||
path: `/preference/${item.category.toLowerCase()}`
|
||||
})
|
||||
},
|
||||
handleCategoryItemClick (item) {
|
||||
this.$router.push({
|
||||
path: item.path
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.restaurants = this.loadAll()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pref-sidebar {
|
||||
-webkit-app-region: drag;
|
||||
background: var(--sideBarBgColor);
|
||||
width: 320px;
|
||||
height: 100vh;
|
||||
padding-top: 40px;
|
||||
box-sizing: border-box;
|
||||
& h3 {
|
||||
margin: 0;
|
||||
font-weight: 100;
|
||||
text-align: center;
|
||||
color: var(--sideBarColor);
|
||||
}
|
||||
}
|
||||
.search-wrapper {
|
||||
-webkit-app-region: no-drag;
|
||||
padding: 0 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
.el-autocomplete {
|
||||
width: 280px;
|
||||
& .el-input__inner {
|
||||
background: transparent;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
}
|
||||
}
|
||||
.pref-autocomplete.el-autocomplete-suggestion {
|
||||
background: var(--floatBgColor);
|
||||
border-color: var(--floatBorderColor);
|
||||
& .el-autocomplete-suggestion__wrap li:hover {
|
||||
background: var(--floatHoverColor);
|
||||
}
|
||||
& .popper__arrow {
|
||||
display: none;
|
||||
}
|
||||
& li {
|
||||
line-height: normal;
|
||||
padding: 7px;
|
||||
opacity: .8;
|
||||
|
||||
& .name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
color: var(--editorColor80);
|
||||
}
|
||||
& .addr {
|
||||
font-size: 12px;
|
||||
color: var(--editorColor);
|
||||
}
|
||||
|
||||
& .highlighted .addr {
|
||||
color: var(--editorColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
.category {
|
||||
-webkit-app-region: no-drag;
|
||||
& .item {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
font-size: 18px;
|
||||
color: var(--sideBarColor);
|
||||
padding-left: 20px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
& > svg {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
fill: var(--iconColor);
|
||||
margin-right: 15px;
|
||||
}
|
||||
&:hover {
|
||||
background: var(--sideBarItemHoverBgColor);
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
width: 4px;
|
||||
height: 0;
|
||||
background: var(--themeColor);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
transition: height .25s ease-in-out;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
&.active {
|
||||
color: var(--sideBarTitleColor);
|
||||
}
|
||||
&.active::before {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
20
src/renderer/prefComponents/theme/config.js
Normal file
@ -0,0 +1,20 @@
|
||||
export const themes = [
|
||||
{
|
||||
name: 'light'
|
||||
},
|
||||
{
|
||||
name: 'dark'
|
||||
},
|
||||
{
|
||||
name: 'graphite'
|
||||
},
|
||||
{
|
||||
name: 'material-dark'
|
||||
},
|
||||
{
|
||||
name: 'ulysses'
|
||||
},
|
||||
{
|
||||
name: 'one-dark'
|
||||
}
|
||||
]
|
178
src/renderer/prefComponents/theme/index.vue
Normal file
@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div class="pref-theme">
|
||||
<h4>Theme</h4>
|
||||
<section class="offcial-themes">
|
||||
<div v-for="t of themes" :key="t.name" class="theme"
|
||||
:class="[t.name, { 'active': t.name === theme }]"
|
||||
@click="handleSelectTheme(t.name)"
|
||||
>
|
||||
<div v-html="t.html"></div>
|
||||
</div>
|
||||
</section>
|
||||
<separator></separator>
|
||||
<section class="import-themes ag-underdevelop">
|
||||
<div>
|
||||
<span>Open the themes folder</span>
|
||||
<el-button size="small">Open Folder</el-button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span>Import custom themes</span>
|
||||
<el-button size="small">Import theme</el-button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import themeMd from './theme.md'
|
||||
import { themes } from './config'
|
||||
import markdownToHtml from '@/util/markdownToHtml'
|
||||
import Separator from '../common/separator'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Separator
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
themes: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
'theme': state => state.preferences.theme
|
||||
})
|
||||
},
|
||||
created () {
|
||||
this.$nextTick(async () => {
|
||||
const newThemes = []
|
||||
for (const theme of themes) {
|
||||
const html = await markdownToHtml(themeMd.replace(/{theme}/, theme.name))
|
||||
newThemes.push({
|
||||
name: theme.name,
|
||||
html
|
||||
})
|
||||
}
|
||||
|
||||
this.themes = newThemes
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
handleSelectTheme (theme) {
|
||||
this.$store.dispatch('SET_SINGLE_PREFERENCE', {
|
||||
type: 'theme',
|
||||
value: theme
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pref-theme {
|
||||
& h4 {
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
font-weight: 100;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
.offcial-themes {
|
||||
& .theme {
|
||||
cursor: pointer;
|
||||
width: 250px;
|
||||
height: 100px;
|
||||
margin: 0px 22px 10px 22px;
|
||||
padding-left: 30px;
|
||||
padding-top: 20px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
background: var(--editorBgColor);
|
||||
color: var(--editorColor);
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(240, 240, 240, .3);
|
||||
border-radius: 5px;
|
||||
&.dark {
|
||||
color: rgba(255, 255, 255, .7);
|
||||
background: #282828;
|
||||
& a {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
&.light {
|
||||
color: rgba(0, 0, 0, .7);
|
||||
background: rgba(255, 255, 255, 1);
|
||||
& a {
|
||||
color: rgba(33, 181, 111, 1);;
|
||||
}
|
||||
}
|
||||
&.graphite {
|
||||
color: rgba(43, 48, 50, .7);
|
||||
background: #f7f7f7;
|
||||
& a {
|
||||
color: rgb(104, 134, 170);
|
||||
}
|
||||
}
|
||||
&.material-dark {
|
||||
color: rgba(171, 178, 191, .8);
|
||||
background: #34393f;
|
||||
& a {
|
||||
color: #f48237;
|
||||
}
|
||||
}
|
||||
&.one-dark {
|
||||
color: #9da5b4;
|
||||
background: #282c34;
|
||||
& a {
|
||||
color: rgba(226, 192, 141, 1);
|
||||
}
|
||||
}
|
||||
&.ulysses {
|
||||
color: rgba(101, 101, 101, .7);
|
||||
background: #f3f3f3;
|
||||
& a {
|
||||
color: rgb(12, 139, 186);
|
||||
}
|
||||
}
|
||||
}
|
||||
& .theme.active {
|
||||
box-shadow: var(--floatShadow);
|
||||
}
|
||||
& h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
color: currentColor;
|
||||
cursor: pointer;
|
||||
&::before {
|
||||
content: 'h3';
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: -20px;
|
||||
display: block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
font-size: 12px;
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
& p {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.import-themes {
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
color: var(--editorColor);
|
||||
& > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& > span {
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
3
src/renderer/prefComponents/theme/theme.md
Normal file
@ -0,0 +1,3 @@
|
||||
### {theme}
|
||||
|
||||
**Lorem Ipsum** is simply [dummy](http://marktext.app) text of the printing and typesetting industry.
|
27
src/renderer/router/index.js
Normal file
@ -0,0 +1,27 @@
|
||||
import App from '@/pages/app'
|
||||
import Preference from '@/pages/preference'
|
||||
import General from '@/prefComponents/general'
|
||||
import Editor from '@/prefComponents/editor'
|
||||
import Markdown from '@/prefComponents/markdown'
|
||||
import Theme from '@/prefComponents/theme'
|
||||
|
||||
const routes = type => ([{
|
||||
path: '/', redirect: type === 'editor'? '/editor' : '/preference'
|
||||
}, {
|
||||
path: '/editor', component: App
|
||||
}, {
|
||||
path: '/preference', component: Preference,
|
||||
children: [{
|
||||
path: '', component: General
|
||||
}, {
|
||||
path: 'general', component: General, name: 'general'
|
||||
}, {
|
||||
path: 'editor', component: Editor, name: 'editor'
|
||||
}, {
|
||||
path: 'markdown', component: Markdown, name: 'markdown'
|
||||
}, {
|
||||
path: 'theme', component: Theme, name: 'theme'
|
||||
}]
|
||||
}])
|
||||
|
||||
export default routes
|
@ -25,9 +25,6 @@ const actions = {
|
||||
ipcRenderer.on('AGANI::view', (e, data) => {
|
||||
commit('SET_MODE', data)
|
||||
})
|
||||
ipcRenderer.on('AGANI::font-setting', e => {
|
||||
bus.$emit('font-setting')
|
||||
})
|
||||
},
|
||||
|
||||
LISTEN_FOR_ABOUT_DIALOG ({ commit }) {
|
||||
|
@ -3,26 +3,36 @@ import { getOptionsFromState } from './help'
|
||||
|
||||
// user preference
|
||||
const state = {
|
||||
theme: 'light',
|
||||
autoSave: true,
|
||||
autoSaveDelay: 3000,
|
||||
titleBarStyle: 'csd',
|
||||
openFilesInNewWindow: false,
|
||||
aidou: true,
|
||||
fileSortBy: 'created',
|
||||
startUp: 'folder',
|
||||
language: 'en',
|
||||
|
||||
editorFontFamily: 'Open Sans',
|
||||
fontSize: '16px',
|
||||
codeFontFamily: 'DejaVu Sans Mono',
|
||||
codeFontSize: '14px',
|
||||
fontSize: 16,
|
||||
lineHeight: 1.6,
|
||||
textDirection: 'ltr',
|
||||
lightColor: '#303133', // color in light theme
|
||||
darkColor: 'rgb(217, 217, 217)', // color in dark theme
|
||||
autoSave: false,
|
||||
preferLooseListItem: true, // prefer loose or tight list items
|
||||
bulletListMarker: '-',
|
||||
codeFontSize: 14,
|
||||
codeFontFamily: 'DejaVu Sans Mono',
|
||||
autoPairBracket: true,
|
||||
autoPairMarkdownSyntax: true,
|
||||
autoPairQuote: true,
|
||||
tabSize: 4,
|
||||
// bullet/list marker width + listIndentation, tab or Daring Fireball Markdown (4 spaces) --> list indentation
|
||||
listIndentation: 1,
|
||||
endOfLine: 'default',
|
||||
textDirection: 'ltr',
|
||||
hideQuickInsertHint: false,
|
||||
titleBarStyle: 'csd',
|
||||
imageDropAction: 'folder',
|
||||
|
||||
preferLooseListItem: true,
|
||||
bulletListMarker: '-',
|
||||
orderListDelimiter: '.',
|
||||
preferHeadingStyle: 'atx',
|
||||
tabSize: 4,
|
||||
listIndentation: 1,
|
||||
|
||||
theme: 'light',
|
||||
// edit modes (they are not in preference.md, but still put them here)
|
||||
typewriter: false, // typewriter mode
|
||||
focus: false, // focus mode
|
||||
@ -49,6 +59,7 @@ const actions = {
|
||||
ipcRenderer.send('mt::ask-for-user-preference')
|
||||
|
||||
ipcRenderer.on('AGANI::user-preference', (e, preference) => {
|
||||
console.log(preference)
|
||||
const { autoSave } = preference
|
||||
commit('SET_USER_PREFERENCE', preference)
|
||||
|
||||
@ -76,9 +87,9 @@ const actions = {
|
||||
})
|
||||
},
|
||||
|
||||
CHANGE_FONT ({ commit }, { type, value }) {
|
||||
commit('SET_USER_PREFERENCE', { [type]: value })
|
||||
// save to preference.md
|
||||
SET_SINGLE_PREFERENCE ({ commit }, { type, value }) {
|
||||
// commit('SET_USER_PREFERENCE', { [type]: value })
|
||||
// save to electron-store
|
||||
ipcRenderer.send('mt::set-user-preference', { [type]: value })
|
||||
}
|
||||
}
|
||||
|
@ -186,3 +186,7 @@ export const hasKeys = obj => Object.keys(obj).length > 0
|
||||
export const cloneObj = (obj, deepCopy=true) => {
|
||||
return deepCopy ? JSON.parse(JSON.stringify(obj)) : Object.assign({}, obj)
|
||||
}
|
||||
|
||||
export const isOsx = process.platform === 'darwin'
|
||||
export const isWindows = process.platform === 'win32'
|
||||
export const isLinux = process.platform === 'linux'
|
||||
|
8
src/renderer/util/markdownToHtml.js
Normal file
@ -0,0 +1,8 @@
|
||||
import ExportHtml from 'muya/lib/utils/exportHtml'
|
||||
|
||||
const markdownToHtml = async markdown => {
|
||||
const html = await new ExportHtml(markdown).renderHtml()
|
||||
return `<article class="markdown-body">${html}</article>`
|
||||
}
|
||||
|
||||
export default markdownToHtml
|
@ -1,6 +1,8 @@
|
||||
import { isLinux, THEME_STYLE_ID, COMMON_STYLE_ID, DEFAULT_CODE_FONT_FAMILY, oneDarkThemes, railscastsThemes } from '../config'
|
||||
import { dark, graphite, materialDark, oneDark, ulysses } from './themeColor'
|
||||
import elementStyle from 'element-ui/lib/theme-chalk/index.css'
|
||||
|
||||
const ORIGINAL_THEME = '#409EFF'
|
||||
const patchTheme = css => {
|
||||
return `@media not print {\n${css}\n}`
|
||||
}
|
||||
@ -11,6 +13,38 @@ const getEmojiPickerPatch = () => {
|
||||
}` : ''
|
||||
}
|
||||
|
||||
const getThemeCluster = themeColor => {
|
||||
const tintColor = (color, tint) => {
|
||||
let red = parseInt(color.slice(1, 3), 16)
|
||||
let green = parseInt(color.slice(3, 5), 16)
|
||||
let blue = parseInt(color.slice(5, 7), 16)
|
||||
if (tint === 0) { // when primary color is in its rgb space
|
||||
return [red, green, blue].join(',')
|
||||
} else {
|
||||
red += Math.round(tint * (255 - red))
|
||||
green += Math.round(tint * (255 - green))
|
||||
blue += Math.round(tint * (255 - blue))
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
return `#${ red }${ green }${ blue }`
|
||||
}
|
||||
}
|
||||
|
||||
const clusters = [{
|
||||
color: themeColor,
|
||||
variable: 'var(--themeColor)'
|
||||
}]
|
||||
for (let i = 9; i >= 1; i--) {
|
||||
clusters.push({
|
||||
color: tintColor(themeColor, Number((i / 10).toFixed(2))),
|
||||
variable: `var(--themeColor${10 - i}0)`
|
||||
})
|
||||
}
|
||||
|
||||
return clusters
|
||||
}
|
||||
|
||||
export const addThemeStyle = theme => {
|
||||
const isCmRailscasts = railscastsThemes.includes(theme)
|
||||
const isCmOneDark = oneDarkThemes.includes(theme)
|
||||
@ -86,13 +120,30 @@ code[class*="language-"],
|
||||
.CodeMirror,
|
||||
pre.ag-paragraph {
|
||||
font-family: ${codeFontFamily}, ${DEFAULT_CODE_FONT_FAMILY};
|
||||
font-size: ${codeFontSize};
|
||||
font-size: ${codeFontSize}px;
|
||||
}
|
||||
|
||||
${getEmojiPickerPatch()}
|
||||
`
|
||||
}
|
||||
|
||||
export const addElementStyle = () => {
|
||||
const ID = 'mt-el-style'
|
||||
let sheet = document.querySelector(`#${ID}`)
|
||||
if (sheet) {
|
||||
return
|
||||
}
|
||||
const themeCluster = getThemeCluster(ORIGINAL_THEME)
|
||||
let newElementStyle = elementStyle
|
||||
for (const { color, variable } of themeCluster) {
|
||||
newElementStyle = newElementStyle.replace(new RegExp(color, 'ig'), variable)
|
||||
}
|
||||
sheet = document.createElement('style')
|
||||
sheet.id = ID
|
||||
document.head.appendChild(sheet)
|
||||
sheet.innerHTML = newElementStyle
|
||||
}
|
||||
|
||||
// Append common sheet and theme at the end of head - order is important.
|
||||
export const addStyles = style => {
|
||||
const { theme } = style
|
||||
|
32
static/preference.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"autoSave": false,
|
||||
"autoSaveDelay": 5000,
|
||||
"titleBarStyle": "custom",
|
||||
"openFilesInNewWindow": false,
|
||||
"aidou": true,
|
||||
"fileSortBy": "created",
|
||||
"startUp": "folder",
|
||||
"language": "en",
|
||||
|
||||
"editorFontFamily": "Open Sans",
|
||||
"fontSize": 16,
|
||||
"lineHeight": 1.6,
|
||||
"codeFontSize": 14,
|
||||
"codeFontFamily": "DejaVu Sans Mono",
|
||||
"autoPairBracket": true,
|
||||
"autoPairMarkdownSyntax": true,
|
||||
"autoPairQuote": true,
|
||||
"endOfLine": "default",
|
||||
"textDirection": "ltr",
|
||||
"hideQuickInsertHint": false,
|
||||
"imageDropAction": "folder",
|
||||
|
||||
"preferLooseListItem": true,
|
||||
"bulletListMarker": "-",
|
||||
"orderListDelimiter": ".",
|
||||
"preferHeadingStyle": "atx",
|
||||
"tabSize": 4,
|
||||
"listIndentation": 1,
|
||||
|
||||
"theme": "light"
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
### :bust_in_silhouette:User Preferences
|
||||
|
||||
Edit and save to update preferences. You can only change the JSON below!
|
||||
|
||||
- **theme**: *String* `dark`, `graphite`, `material-dark`, `one-dark`, `light` or `ulysses`
|
||||
|
||||
- **autoSave**: *Boolean* `true` or `false`
|
||||
|
||||
- **endOfLine**: *String* `lf`, `crlf` or `default`
|
||||
|
||||
- **listIndentation**: `"dfm"`, `"tab"` or number (`1-4`)
|
||||
|
||||
- **bulletListMarker**: *String* `+`,`-` or `*`
|
||||
|
||||
- **textDirection**: *String* `ltr` or `rtl`
|
||||
|
||||
- **titleBarStyle**: *String* `csd` (macOS only), `custom` or `native`
|
||||
|
||||
```json
|
||||
{
|
||||
"fontSize": "16px",
|
||||
"editorFontFamily": "Open Sans",
|
||||
"codeFontFamily": "DejaVu Sans Mono",
|
||||
"codeFontSize": "14px",
|
||||
"lightColor": "#303133",
|
||||
"darkColor": "rgb(217, 217, 217)",
|
||||
"lineHeight": "1.6",
|
||||
"theme": "light",
|
||||
"autoSave": false,
|
||||
"aidou": false,
|
||||
"hideQuickInsertHint": false,
|
||||
"preferLooseListItem": true,
|
||||
"bulletListMarker": "-",
|
||||
"autoPairBracket": true,
|
||||
"autoPairMarkdownSyntax": true,
|
||||
"autoPairQuote": true,
|
||||
"endOfLine": "default",
|
||||
"tabSize": 4,
|
||||
"listIndentation": 1,
|
||||
"textDirection": "ltr",
|
||||
"titleBarStyle": "csd",
|
||||
"openFilesInNewWindow": true
|
||||
}
|
||||
```
|
||||
|
||||
More user preferences coming soon.
|
||||
|
||||
**Please use `Cmd + S`/`Ctrl + S` to save your preferences and reload Mark Text to use your setting!**
|
||||
|
||||
> Your friends at Mark Text.
|
74
yarn.lock
@ -422,7 +422,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.0:
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d"
|
||||
integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==
|
||||
|
||||
ajv@^6.1.0, ajv@^6.5.5, ajv@^6.9.1, ajv@^6.9.2:
|
||||
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.5.5, ajv@^6.9.1, ajv@^6.9.2:
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1"
|
||||
integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==
|
||||
@ -2586,6 +2586,19 @@ concat-stream@1.6.2, concat-stream@^1.5.0:
|
||||
readable-stream "^2.2.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
conf@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/conf/-/conf-4.0.2.tgz#cc25649295259fc77f4606840b7718cca5c1d5bd"
|
||||
integrity sha512-SVEWGdAlA+BNfpJ5vF7S6SbkXA7Qk1ycWV6+UAWkC3vQvjxx7xxuYNriKnShjlbIPStUiTK0MZWdQYYtTYdwZw==
|
||||
dependencies:
|
||||
ajv "^6.10.0"
|
||||
dot-prop "^5.0.0"
|
||||
env-paths "^2.2.0"
|
||||
json-schema-typed "^7.0.0"
|
||||
make-dir "^3.0.0"
|
||||
pkg-up "^3.0.1"
|
||||
write-file-atomic "^2.4.2"
|
||||
|
||||
configstore@^3.0.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f"
|
||||
@ -3790,6 +3803,13 @@ dot-prop@^4.1.0:
|
||||
dependencies:
|
||||
is-obj "^1.0.0"
|
||||
|
||||
dot-prop@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.0.0.tgz#64b7968af349c3a9f966aa12658dbd5829f6b953"
|
||||
integrity sha512-RTmaF2jx3nOBO2GvtFqjnDLycjFUMqt+2pwRx7JVYa81lDauoj9aNkyrJI2ikR58FbBIchiIlRiGG+muLJ4oHQ==
|
||||
dependencies:
|
||||
is-obj "^1.0.0"
|
||||
|
||||
dotenv-expand@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-4.2.0.tgz#def1f1ca5d6059d24a766e587942c21106ce1275"
|
||||
@ -3981,6 +4001,14 @@ electron-rebuild@^1.8.4:
|
||||
spawn-rx "^3.0.0"
|
||||
yargs "^12.0.5"
|
||||
|
||||
electron-store@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-3.2.0.tgz#50d2d6677beb46293c15814f2d230b65c6ca555e"
|
||||
integrity sha512-+goKW06sPo8KyPd9ctozRQGjctJ+M4qDpZ0Dx02X08AMf6lSlHQRmYHMYbKXpVpoq4y250wRfEHNxtP72HbfVQ==
|
||||
dependencies:
|
||||
conf "^4.0.1"
|
||||
type-fest "^0.3.1"
|
||||
|
||||
electron-to-chromium@^1.3.124, electron-to-chromium@^1.3.47:
|
||||
version "1.3.127"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.127.tgz#9b34d3d63ee0f3747967205b953b25fe7feb0e10"
|
||||
@ -4136,6 +4164,11 @@ env-paths@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0"
|
||||
integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=
|
||||
|
||||
env-paths@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43"
|
||||
integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==
|
||||
|
||||
errno@^0.1.3, errno@~0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
||||
@ -6359,6 +6392,11 @@ json-schema-traverse@^0.4.1:
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json-schema-typed@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.0.tgz#714f3bb539637644b8cb9c99a097c4ee8f8e8c8f"
|
||||
integrity sha512-ikVqF4dlAgRvAb3MDAgDQRtB/GIC8+iq+z5bczPh9bUT7bAZCdGfGCypJHBquzZNoxebql1UgPxWbImnvkSuJg==
|
||||
|
||||
json-schema@0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
|
||||
@ -7036,6 +7074,13 @@ make-dir@^2.0.0:
|
||||
pify "^4.0.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
make-dir@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801"
|
||||
integrity sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==
|
||||
dependencies:
|
||||
semver "^6.0.0"
|
||||
|
||||
mamacro@^0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4"
|
||||
@ -8272,6 +8317,13 @@ pkg-dir@^3.0.0:
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
pkg-up@^3.0.1:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
||||
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
plist@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c"
|
||||
@ -8968,6 +9020,14 @@ raw-body@2.4.0:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
raw-loader@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npm.taobao.org/raw-loader/download/raw-loader-2.0.0.tgz#e2813d9e1e3f80d1bbade5ad082e809679e20c26"
|
||||
integrity sha1-4oE9nh4/gNG7reWtCC6AlnniDCY=
|
||||
dependencies:
|
||||
loader-utils "^1.1.0"
|
||||
schema-utils "^1.0.0"
|
||||
|
||||
rc@^1.0.1, rc@^1.1.6, rc@^1.2.1, rc@^1.2.7:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||
@ -10723,6 +10783,11 @@ type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
||||
type-fest@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
|
||||
integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==
|
||||
|
||||
type-func@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/type-func/-/type-func-1.0.3.tgz#ab184234ae80d8d50057cefeff3b2d97d08ae9b0"
|
||||
@ -11437,6 +11502,11 @@ vue-loader@^15.7.0:
|
||||
vue-hot-reload-api "^2.3.0"
|
||||
vue-style-loader "^4.1.0"
|
||||
|
||||
vue-router@^3.0.6:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.6.tgz#2e4f0f9cbb0b96d0205ab2690cfe588935136ac3"
|
||||
integrity sha512-Ox0ciFLswtSGRTHYhGvx2L44sVbTPNS+uD2kRISuo8B39Y79rOo0Kw0hzupTmiVtftQYCZl87mwldhh2L9Aquw==
|
||||
|
||||
vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8"
|
||||
@ -11826,7 +11896,7 @@ wrappy@1:
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
write-file-atomic@^2.0.0:
|
||||
write-file-atomic@^2.0.0, write-file-atomic@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.2.tgz#a7181706dfba17855d221140a9c06e15fcdd87b9"
|
||||
integrity sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==
|
||||
|