diff --git a/CHANGELOG.md b/CHANGELOG.md index 106c6cca..35c98924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Add Typewriter Mode, The current line will always in the center of the document. If you change the current line, it will be auto scroll to the new line. +- Add Focus Mode, the current paragraph's will be focused. + **Optimization** - Optimize the display of path name and file name in title bar. diff --git a/Untitled - unsaved b/Untitled - unsaved index ade202ad..0695d91f 100644 --- a/Untitled - unsaved +++ b/Untitled - unsaved @@ -1,15 +1 @@ -wow - - - -owoww - - - - - -owow - - - - +wowowo wowowow diff --git a/src/editor/codeMirror/index.js b/src/editor/codeMirror/index.js index 9019b66b..b08666f6 100644 --- a/src/editor/codeMirror/index.js +++ b/src/editor/codeMirror/index.js @@ -1,14 +1,17 @@ import 'codemirror/addon/edit/closebrackets' import 'codemirror/addon/edit/closetag' +import 'codemirror/addon/selection/active-line' import 'codemirror/mode/meta' import codeMirror from 'codemirror/lib/codemirror' import loadmode from './loadmode' +import overlayMode from './overlayMode' import languages from './modes' import 'codemirror/lib/codemirror.css' import './index.css' loadmode(codeMirror) +overlayMode(codeMirror) window.CodeMirror = codeMirror const modes = codeMirror.modeInfo diff --git a/src/editor/codeMirror/overlayMode.js b/src/editor/codeMirror/overlayMode.js new file mode 100644 index 00000000..18e8ba5e --- /dev/null +++ b/src/editor/codeMirror/overlayMode.js @@ -0,0 +1,98 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// Utility function that allows modes to be combined. The mode given +// as the base argument takes care of most of the normal mode +// functionality, but a second (typically simple) mode is used, which +// can override the style of text. Both modes get to parse all of the +// text, but when both assign a non-null style to a piece of code, the +// overlay wins, unless the combine argument was true and not overridden, +// or state.overlay.combineTokens was true, in which case the styles are +// combined. + +const overlayMode = CodeMirror => { + CodeMirror.overlayMode = function (base, overlay, combine) { + return { + startState () { + return { + base: CodeMirror.startState(base), + overlay: CodeMirror.startState(overlay), + basePos: 0, + baseCur: null, + overlayPos: 0, + overlayCur: null, + streamSeen: null + } + }, + + copyState (state) { + return { + base: CodeMirror.copyState(base, state.base), + overlay: CodeMirror.copyState(overlay, state.overlay), + basePos: state.basePos, + baseCur: null, + overlayPos: state.overlayPos, + overlayCur: null + } + }, + + token (stream, state) { + if (stream !== state.streamSeen || + Math.min(state.basePos, state.overlayPos) < stream.start) { + state.streamSeen = stream + state.basePos = state.overlayPos = stream.start + } + + if (stream.start === state.basePos) { + state.baseCur = base.token(stream, state.base) + state.basePos = stream.pos + } + + if (stream.start === state.overlayPos) { + stream.pos = stream.start + state.overlayCur = overlay.token(stream, state.overlay) + state.overlayPos = stream.pos + } + + stream.pos = Math.min(state.basePos, state.overlayPos) + + // state.overlay.combineTokens always takes precedence over combine, + // unless set to null + if (state.overlayCur === null) { + return state.baseCur + } else if ( + (state.baseCur !== null && + state.overlay.combineTokens) || + (combine && state.overlay.combineTokens === null) + ) { + return state.baseCur + ' ' + state.overlayCur + } else return state.overlayCur + }, + + indent: base.indent && function (state, textAfter) { + return base.indent(state.base, textAfter) + }, + + electricChars: base.electricChars, + + innerMode (state) { + return { + state: state.base, + mode: base + } + }, + + blankLine (state) { + var baseToken, overlayToken + if (base.blankLine) baseToken = base.blankLine(state.base) + if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay) + + return overlayToken == null + ? baseToken + : (combine && baseToken != null ? baseToken + ' ' + overlayToken : overlayToken) + } + } + } +} + +export default overlayMode diff --git a/src/editor/index.css b/src/editor/index.css index c4bc7a57..2d614549 100644 --- a/src/editor/index.css +++ b/src/editor/index.css @@ -135,8 +135,8 @@ li.ag-task-list-item > input[type=checkbox] { height: inherit; margin: 4px 0px 0px; top: 1px; - width: 19px; - height: 19px; + width: 18px; + height: 18px; left: -20px; } @@ -146,8 +146,8 @@ li.ag-task-list-item > input.ag-checkbox-checked ~ p { li.ag-task-list-item > input[type=checkbox]::before { content: ''; - width: 16px; - height: 16px; + width: 14px; + height: 14px; box-sizing: border-box; display: inline-block; border: 2px solid #606266; @@ -174,7 +174,7 @@ li.ag-task-list-item > input.ag-checkbox-checked::after { position: absolute; display: inline-block; top: 4px; - left: 4px; + left: 3px; } li p .ag-hide:first-child { diff --git a/src/editor/index.js b/src/editor/index.js index eafb3dfe..2b14cabe 100644 --- a/src/editor/index.js +++ b/src/editor/index.js @@ -390,10 +390,12 @@ class Aganippe { return this.contentState.wordCount() } - setMarkdown (text) { - // if text is blank, dont need to import markdown - if (!text.trim()) return - this.contentState.importMarkdown(text) + setMarkdown (markdown, cursor) { + // if markdown is blank, dont need to import markdown + if (!markdown.trim()) return + this.contentState.importMarkdown(markdown) + this.contentState.importCursor(cursor) + this.contentState.render() this.dispatchChange() } diff --git a/src/editor/utils/importMarkdown.js b/src/editor/utils/importMarkdown.js index f36b24f1..601c8154 100644 --- a/src/editor/utils/importMarkdown.js +++ b/src/editor/utils/importMarkdown.js @@ -226,21 +226,33 @@ const importRegister = ContentState => { return this.getStateFragment(markdown) } + ContentState.prototype.importCursor = function (cursor) { + // set cursor + if (cursor) { + // TODO for codeMirror cursor to aganippe cursor + const lastBlock = this.getLastBlock() + const key = lastBlock.key + const offset = lastBlock.text.length + this.cursor = { + start: { key, offset }, + end: { key, offset } + } + } else { + const lastBlock = this.getLastBlock() + const key = lastBlock.key + const offset = lastBlock.text.length + this.cursor = { + start: { key, offset }, + end: { key, offset } + } + } + } + ContentState.prototype.importMarkdown = function (markdown) { // empty the blocks and codeBlocks this.keys = new Set() this.codeBlocks = new Map() this.blocks = this.getStateFragment(markdown) - // set cursor - const lastBlock = this.getLastBlock() - const key = lastBlock.key - const offset = lastBlock.text.length - this.cursor = { - start: { key, offset }, - end: { key, offset } - } - // re-render - this.render() } } diff --git a/src/main/actions/view.js b/src/main/actions/view.js index c5b2d0f0..33752a0b 100644 --- a/src/main/actions/view.js +++ b/src/main/actions/view.js @@ -10,6 +10,15 @@ const HASH = { export const view = (win, item, type) => { const { checked } = item win.webContents.send('AGANI::view', { type, checked }) + + if (type === 'sourceCode') { + const viewMenuItem = getMenuItem('View') + viewMenuItem.submenu.items.forEach(item => { + if (/typewriter|focus/.test(HASH[item.label])) { + item.enabled = !checked + } + }) + } } ipcMain.on('AGANI::ask-for-mode', e => { diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 43ec12bf..e6372c3e 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -9,8 +9,18 @@ :typewriter="typewriter" :focus="focus" :source-code="sourceCode" + :markdown="markdown" + :cursor="cursor" + v-if="!sourceCode" > - + + @@ -18,6 +28,7 @@ import Editor from '@/components/editor' import TitleBar from '@/components/titleBar' import Search from '@/components/search' + import SourceCode from '@/components/sourceCode' import { mapState } from 'vuex' export default { @@ -25,13 +36,14 @@ components: { Editor, TitleBar, - Search + Search, + SourceCode }, data () { return {} }, computed: { - ...mapState(['pathname', 'windowActive', 'wordCount', 'typewriter', 'focus', 'sourceCode']) + ...mapState(['pathname', 'windowActive', 'wordCount', 'typewriter', 'focus', 'sourceCode', 'markdown', 'cursor']) }, created () { const { dispatch } = this.$store @@ -56,4 +68,10 @@ .editor-container { padding-top: 22px; } + .editor-container .hide { + z-index: -1; + opacity: 0; + position: absolute; + left: -10000px; + } diff --git a/src/renderer/components/Editor.vue b/src/renderer/components/Editor.vue index 74200f0e..13facdc6 100644 --- a/src/renderer/components/Editor.vue +++ b/src/renderer/components/Editor.vue @@ -1,7 +1,7 @@