diff --git a/.babelrc b/.babelrc index 9c2de33f..e8c16d89 100644 --- a/.babelrc +++ b/.babelrc @@ -42,8 +42,8 @@ } }, "plugins": [["component", { - "libraryName": "element-ui", - "styleLibraryName": "theme-chalk" + "style": false, + "libraryName": "element-ui" } ], "transform-runtime"] } diff --git a/.electron-vue/webpack.renderer.config.js b/.electron-vue/webpack.renderer.config.js index ae3fc444..d4613346 100644 --- a/.electron-vue/webpack.renderer.config.js +++ b/.electron-vue/webpack.renderer.config.js @@ -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' + ] } ] }, diff --git a/doc/Block addition properties and its value.md b/doc/BLOCK_ADDITION_PROPERTY.md similarity index 100% rename from doc/Block addition properties and its value.md rename to doc/BLOCK_ADDITION_PROPERTY.md diff --git a/doc/PREFERENCE.md b/doc/PREFERENCE.md new file mode 100644 index 00000000..e7a7207c --- /dev/null +++ b/doc/PREFERENCE.md @@ -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` | diff --git a/doc/SETTINGS.md b/doc/SETTINGS.md deleted file mode 100644 index e0f68191..00000000 --- a/doc/SETTINGS.md +++ /dev/null @@ -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** diff --git a/package.json b/package.json index 8dc882bc..f631f1b9 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/main/app/index.js b/src/main/app/index.js index 3c29f7db..8c168ed4 100644 --- a/src/main/app/index.js +++ b/src/main/app/index.js @@ -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 => { diff --git a/src/main/app/windowManager.js b/src/main/app/windowManager.js index db320496..c4478894 100644 --- a/src/main/app/windowManager.js +++ b/src/main/app/windowManager.js @@ -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) + } } }) } diff --git a/src/main/config.js b/src/main/config.js index f04e841a..560315c0 100644 --- a/src/main/config.js +++ b/src/main/config.js @@ -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/ diff --git a/src/main/menu/actions/view.js b/src/main/menu/actions/view.js index 1261efbf..9494d04a 100644 --- a/src/main/menu/actions/view.js +++ b/src/main/menu/actions/view.js @@ -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 }) } diff --git a/src/main/menu/index.js b/src/main/menu/index.js index d3659ec1..b2352fa9 100644 --- a/src/main/menu/index.js +++ b/src/main/menu/index.js @@ -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) + } + }) } } diff --git a/src/main/menu/templates/edit.js b/src/main/menu/templates/edit.js index a74d3402..fb60fa30 100755 --- a/src/main/menu/templates/edit.js +++ b/src/main/menu/templates/edit.js @@ -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') diff --git a/src/main/menu/templates/file.js b/src/main/menu/templates/file.js index a45a1d44..b39f50af 100755 --- a/src/main/menu/templates/file.js +++ b/src/main/menu/templates/file.js @@ -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) } diff --git a/src/main/menu/templates/index.js b/src/main/menu/templates/index.js index 370ff884..d9fa4962 100644 --- a/src/main/menu/templates/index.js +++ b/src/main/menu/templates/index.js @@ -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. * diff --git a/src/main/menu/templates/view.js b/src/main/menu/templates/view.js index 02ca1ed0..96c6520f 100755 --- a/src/main/menu/templates/view.js +++ b/src/main/menu/templates/view.js @@ -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', diff --git a/src/main/preferences/index.js b/src/main/preferences/index.js index 71a16235..1416120b 100644 --- a/src/main/preferences/index.js +++ b/src/main/preferences/index.js @@ -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) }) } } diff --git a/src/main/preferences/schema.json b/src/main/preferences/schema.json new file mode 100644 index 00000000..9f53ac96 --- /dev/null +++ b/src/main/preferences/schema.json @@ -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" + } +} diff --git a/src/main/windows/base.js b/src/main/windows/base.js new file mode 100644 index 00000000..f4e51b7e --- /dev/null +++ b/src/main/windows/base.js @@ -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 diff --git a/src/main/windows/editor.js b/src/main/windows/editor.js index 5545c039..c7cc42ad 100644 --- a/src/main/windows/editor.js +++ b/src/main/windows/editor.js @@ -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 diff --git a/src/main/windows/setting.js b/src/main/windows/setting.js new file mode 100644 index 00000000..6b91125f --- /dev/null +++ b/src/main/windows/setting.js @@ -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 diff --git a/src/muya/lib/config/index.js b/src/muya/lib/config/index.js index 402823c9..25872d7f 100644 --- a/src/muya/lib/config/index.js +++ b/src/muya/lib/config/index.js @@ -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, diff --git a/src/muya/lib/contentState/inputCtrl.js b/src/muya/lib/contentState/inputCtrl.js index 6308b1fa..1dcf09a3 100644 --- a/src/muya/lib/contentState/inputCtrl.js +++ b/src/muya/lib/contentState/inputCtrl.js @@ -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) diff --git a/src/muya/lib/contentState/paragraphCtrl.js b/src/muya/lib/contentState/paragraphCtrl.js index f250d833..a9ac1af2 100644 --- a/src/muya/lib/contentState/paragraphCtrl.js +++ b/src/muya/lib/contentState/paragraphCtrl.js @@ -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') || diff --git a/src/muya/lib/contentState/updateCtrl.js b/src/muya/lib/contentState/updateCtrl.js index a5b21a45..6bda3e30 100644 --- a/src/muya/lib/contentState/updateCtrl.js +++ b/src/muya/lib/contentState/updateCtrl.js @@ -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 diff --git a/src/muya/lib/index.js b/src/muya/lib/index.js index 71d6ed32..c772bb21 100644 --- a/src/muya/lib/index.js +++ b/src/muya/lib/index.js @@ -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 () { diff --git a/src/muya/themes/default.css b/src/muya/themes/default.css index dbb05e6e..f2670119 100644 --- a/src/muya/themes/default.css +++ b/src/muya/themes/default.css @@ -150,7 +150,7 @@ kbd { } .v-modal { - background: var(--maskColor); + background: var(--maskColor) !important; } body>*:first-child { diff --git a/src/renderer/assets/icons/pref_editor.svg b/src/renderer/assets/icons/pref_editor.svg new file mode 100644 index 00000000..dd612acd --- /dev/null +++ b/src/renderer/assets/icons/pref_editor.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/assets/icons/pref_email.svg b/src/renderer/assets/icons/pref_email.svg new file mode 100644 index 00000000..d12e9705 --- /dev/null +++ b/src/renderer/assets/icons/pref_email.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/assets/icons/pref_fontsize.svg b/src/renderer/assets/icons/pref_fontsize.svg new file mode 100644 index 00000000..dc882d23 --- /dev/null +++ b/src/renderer/assets/icons/pref_fontsize.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/assets/icons/pref_general.svg b/src/renderer/assets/icons/pref_general.svg new file mode 100644 index 00000000..845ce188 --- /dev/null +++ b/src/renderer/assets/icons/pref_general.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/assets/icons/pref_lineheight.svg b/src/renderer/assets/icons/pref_lineheight.svg new file mode 100644 index 00000000..1fcd1128 --- /dev/null +++ b/src/renderer/assets/icons/pref_lineheight.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/assets/icons/pref_markdown.svg b/src/renderer/assets/icons/pref_markdown.svg new file mode 100644 index 00000000..fee3d72d --- /dev/null +++ b/src/renderer/assets/icons/pref_markdown.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/assets/icons/pref_theme.svg b/src/renderer/assets/icons/pref_theme.svg new file mode 100644 index 00000000..69776db2 --- /dev/null +++ b/src/renderer/assets/icons/pref_theme.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/assets/styles/index.css b/src/renderer/assets/styles/index.css index f7f328b8..be64dfd0 100644 --- a/src/renderer/assets/styles/index.css +++ b/src/renderer/assets/styles/index.css @@ -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; +} diff --git a/src/renderer/assets/themes/dark.theme.css b/src/renderer/assets/themes/dark.theme.css index e09130a2..a553ad4f 100644 --- a/src/renderer/assets/themes/dark.theme.css +++ b/src/renderer/assets/themes/dark.theme.css @@ -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); diff --git a/src/renderer/assets/themes/graphite.theme.css b/src/renderer/assets/themes/graphite.theme.css index 45eeb5bb..8b78cedb 100644 --- a/src/renderer/assets/themes/graphite.theme.css +++ b/src/renderer/assets/themes/graphite.theme.css @@ -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); diff --git a/src/renderer/assets/themes/material-dark.theme.css b/src/renderer/assets/themes/material-dark.theme.css index 7ff4e8ad..2851c99b 100644 --- a/src/renderer/assets/themes/material-dark.theme.css +++ b/src/renderer/assets/themes/material-dark.theme.css @@ -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); diff --git a/src/renderer/assets/themes/one-dark.theme.css b/src/renderer/assets/themes/one-dark.theme.css index bf55576e..73dc42dd 100644 --- a/src/renderer/assets/themes/one-dark.theme.css +++ b/src/renderer/assets/themes/one-dark.theme.css @@ -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; diff --git a/src/renderer/assets/themes/ulysses.theme.css b/src/renderer/assets/themes/ulysses.theme.css index 6a5988ec..424428ab 100644 --- a/src/renderer/assets/themes/ulysses.theme.css +++ b/src/renderer/assets/themes/ulysses.theme.css @@ -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); diff --git a/src/renderer/bootstrap.js b/src/renderer/bootstrap.js index ccfbf40e..d635b17d 100644 --- a/src/renderer/bootstrap.js +++ b/src/renderer/bootstrap.js @@ -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 diff --git a/src/renderer/components/editorWithTabs/editor.vue b/src/renderer/components/editorWithTabs/editor.vue index 30684398..daca8fc2 100644 --- a/src/renderer/components/editorWithTabs/editor.vue +++ b/src/renderer/components/editorWithTabs/editor.vue @@ -2,7 +2,7 @@