diff --git a/.gitignore b/.gitignore index ee5430eb..8d2c0503 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .env node_modules/* !node_modules/boost -Boost-darwin-x64/ -backup/ +dist/ compiled diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js index 4a4b7f0d..e11c140b 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail.js @@ -162,6 +162,7 @@ export default class ArticleDetail extends React.Component { } handleClipboardButtonClick (e) { + activityRecord.emit('MAIN_DETAIL_COPY') clipboard.writeText(this.props.activeArticle.content) notify('Saved to Clipboard!', { body: 'Paste it wherever you want!' @@ -293,9 +294,9 @@ export default class ArticleDetail extends React.Component { if (newArticle.title.length === 0) { newArticle.title = `Created at ${moment(newArticle.createdAt).format('YYYY/MM/DD HH:mm')}` } - activityRecord.emit('ARTICLE_CREATE') + activityRecord.emit('ARTICLE_CREATE', {mode: newArticle.mode}) } else { - activityRecord.emit('ARTICLE_UPDATE') + activityRecord.emit('ARTICLE_UPDATE', {mode: newArticle.mode}) } dispatch(updateArticle(newArticle)) @@ -435,7 +436,34 @@ export default class ArticleDetail extends React.Component { handleTogglePreviewButtonClick (e) { if (this.state.article.mode === 'markdown') { - this.setState({previewMode: !this.state.previewMode}) + if (!this.state.previewMode) { + let cursorPosition = this.refs.code.getCursorPosition() + let firstVisibleRow = this.refs.code.getFirstVisibleRow() + this.setState({ + previewMode: true, + cursorPosition, + firstVisibleRow + }, function () { + let previewEl = ReactDOM.findDOMNode(this.refs.preview) + let anchors = previewEl.querySelectorAll('.lineAnchor') + for (let i = 0; i < anchors.length; i++) { + if (parseInt(anchors[i].dataset.key, 10) > cursorPosition.row || i === anchors.length - 1) { + var targetAnchor = anchors[i > 0 ? i - 1 : 0] + previewEl.scrollTop = targetAnchor.offsetTop - 100 + break + } + } + }) + } else { + this.setState({ + previewMode: false + }, function () { + console.log(this.state.cursorPosition) + this.refs.code.moveCursorTo(this.state.cursorPosition.row, this.state.cursorPosition.column) + this.refs.code.scrollToLine(this.state.firstVisibleRow) + this.refs.code.editor.focus() + }) + } } } @@ -524,7 +552,7 @@ export default class ArticleDetail extends React.Component { {this.state.previewMode - ? + ? : ( this.handleContentChange(e, value)} diff --git a/browser/styles/mixins/marked.styl b/browser/styles/mixins/marked.styl index 38f43318..8a2695d1 100644 --- a/browser/styles/mixins/marked.styl +++ b/browser/styles/mixins/marked.styl @@ -1,36 +1,38 @@ marked() - h1, h2, h3, h4, h5, h6, p - &:first-child - margin-top 0 hr border-top none border-bottom solid 1px borderColor margin 15px 0 + h1, h2, h3, h4, h5, h6 + margin 0 0 15px + font-weight 600 + * + h1, * + h2, * + h3, * + h4, * + h5, * + h6 + margin-top 25px h1 font-size 2em border-bottom solid 2px borderColor - margin 0.33em auto 0.67em + line-height 2.333em h2 - font-size 1.5em - margin 0.42em auto 0.83em + font-size 1.66em + line-height 2.07em h3 - font-size 1.17em - margin 0.5em auto 1em + font-size 1.33em + line-height 1.6625em h4 - font-size 1em - margin 0.67em auto 1.33em + font-size 1.15em + line-height 1.4375em h5 - font-size 0.83em - margin 0.84em auto 1.67em + font-size 1em + line-height 1.25em h6 - font-size 0.67em - margin 1.16em auto 2.33em - h1, h2, h3, h4, h5, h6 - font-weight 700 - line-height 1.8em + font-size 0.8em + line-height 1em + + * + p, * + blockquote, * + ul, * + ol, * + pre + margin-top 15px p - line-height 1.8em - margin 15px 0 25px + line-height 1.9em + margin 0 0 15px img max-width 100% strong @@ -41,15 +43,17 @@ marked() text-decoration line-through blockquote border-left solid 4px brandBorderColor - margin 15px 0 25px + margin 0 0 15px padding 0 25px ul list-style-type disc padding-left 35px - margin-bottom 35px + margin-bottom 15px li display list-item line-height 1.8em + &>li>ul, &>li>ol + margin 0 &>li>ul list-style-type circle &>li>ul @@ -57,10 +61,12 @@ marked() ol list-style-type decimal padding-left 35px - margin-bottom 35px + margin-bottom 15px li display list-item line-height 1.8em + &>li>ul, &>li>ol + margin 0 code font-family Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace; padding 2px 4px @@ -70,15 +76,19 @@ marked() color black text-decoration none background-color #F6F6F6 + margin-right 2px + * + code + margin-left 2px pre padding 5px border solid 1px borderColor border-radius 5px overflow-x auto - margin 15px 0 25px + margin 0 0 15px background-color #F6F6F6 line-height 1.35em &>code + margin 0 padding 0 border none border-radius 0 diff --git a/builder-config.json b/builder-config.json new file mode 100644 index 00000000..8b8fa269 --- /dev/null +++ b/builder-config.json @@ -0,0 +1,12 @@ +{ + "osx" : { + "title": "Boost Installer", + // "background": "resources/background.png", + "icon": "resources/app.icns", + "icon-size": 80, + "contents": [ + { "x": 438, "y": 344, "type": "link", "path": "/Applications" }, + { "x": 192, "y": 344, "type": "file" } + ] + } +} diff --git a/lib/activityRecord.js b/lib/activityRecord.js index b79e2447..79036010 100644 --- a/lib/activityRecord.js +++ b/lib/activityRecord.js @@ -4,6 +4,9 @@ import keygen from 'boost/keygen' import dataStore from 'boost/dataStore' import { request, WEB_URL } from 'boost/api' +const electron = require('electron') +const version = electron.remote.app.getVersion() + function isSameDate (a, b) { a = moment(a).utcOffset(+540).format('YYYYMMDD') b = moment(b).utcOffset(+540).format('YYYYMMDD') @@ -16,6 +19,7 @@ export function init () { if (records == null) { saveAllRecords([]) } + emit(null) postRecords() if (window != null) { @@ -81,7 +85,7 @@ export function postRecords (data) { }) } -export function emit (type, data) { +export function emit (type, data = {}) { let records = getAllRecords() let index = _.findIndex(records, record => { @@ -94,7 +98,6 @@ export function emit (type, data) { records.push(todayRecord) } else todayRecord = records[index] - console.log(type) switch (type) { case 'ARTICLE_CREATE': case 'ARTICLE_UPDATE': @@ -104,6 +107,7 @@ export function emit (type, data) { case 'FOLDER_DESTROY': case 'FINDER_OPEN': case 'FINDER_COPY': + case 'MAIN_DETAIL_COPY': todayRecord[type] = todayRecord[type] == null ? 1 : todayRecord[type] + 1 @@ -111,9 +115,26 @@ export function emit (type, data) { break } + // Count ARTICLE_CREATE and ARTICLE_UPDATE again by syntax + if ((type === 'ARTICLE_CREATE' || type === 'ARTICLE_UPDATE') && data.mode != null) { + let recordKey = type + '_BY_SYNTAX' + if (todayRecord[recordKey] == null) todayRecord[recordKey] = {} + + todayRecord[recordKey][data.mode] = todayRecord[recordKey][data.mode] == null + ? 1 + : todayRecord[recordKey][data.mode] + 1 + } + let storeData = dataStore.getData() todayRecord.FOLDER_COUNT = _.isArray(storeData.folders) ? storeData.folders.length : 0 todayRecord.ARTICLE_COUNT = _.isArray(storeData.articles) ? storeData.articles.length : 0 + todayRecord.CLIENT_VERSION = version + + todayRecord.SYNTAX_COUNT = storeData.articles.reduce((sum, article) => { + if (sum[article.mode] == null) sum[article.mode] = 1 + else sum[article.mode]++ + return sum + }, {}) saveAllRecords(records) } diff --git a/lib/components/CodeEditor.js b/lib/components/CodeEditor.js index d82ca3f2..e6941ed8 100644 --- a/lib/components/CodeEditor.js +++ b/lib/components/CodeEditor.js @@ -30,6 +30,7 @@ module.exports = React.createClass({ editor.renderer.setShowGutter(true) editor.setTheme('ace/theme/xcode') editor.clearSelection() + editor.moveCursorTo(0, 0) editor.setReadOnly(!!this.props.readOnly) @@ -50,16 +51,14 @@ module.exports = React.createClass({ this.props.onChange(e, value) } }.bind(this)) - - this.setState({editor: editor}) }, componentDidUpdate: function (prevProps) { - if (this.state.editor.getValue() !== this.props.code) { - this.state.editor.setValue(this.props.code) - this.state.editor.clearSelection() + if (this.editor.getValue() !== this.props.code) { + this.editor.setValue(this.props.code) + this.editor.clearSelection() } if (prevProps.mode !== this.props.mode) { - var session = this.state.editor.getSession() + var session = this.editor.getSession() let mode = _.findWhere(modes, {name: this.props.mode}) let syntaxMode = mode != null ? mode.mode @@ -67,6 +66,18 @@ module.exports = React.createClass({ session.setMode('ace/mode/' + syntaxMode) } }, + getFirstVisibleRow: function () { + return this.editor.getFirstVisibleRow() + }, + getCursorPosition: function () { + return this.editor.getCursorPosition() + }, + moveCursorTo: function (row, col) { + this.editor.moveCursorTo(row, col) + }, + scrollToLine: function (num) { + this.editor.scrollToLine(num, false, false) + }, render: function () { return (
diff --git a/lib/components/ModeSelect.js b/lib/components/ModeSelect.js index 28404d20..09dae7e6 100644 --- a/lib/components/ModeSelect.js +++ b/lib/components/ModeSelect.js @@ -161,8 +161,8 @@ export default class ModeSelect extends React.Component { let filteredOptions = modes .filter(mode => { let search = this.state.search - let nameMatched = mode.name.match(search) - let aliasMatched = _.some(mode.alias, alias => alias.match(search)) + let nameMatched = mode.name.match(_.escapeRegExp(search)) + let aliasMatched = _.some(mode.alias, alias => alias.match(_.escapeRegExp(search))) return nameMatched || aliasMatched }) .map((mode, index) => { diff --git a/lib/markdown.js b/lib/markdown.js index b27830ea..619b9655 100644 --- a/lib/markdown.js +++ b/lib/markdown.js @@ -8,20 +8,31 @@ var md = markdownit({ highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { - return hljs.highlight(lang, str).value; + return hljs.highlight(lang, str).value } catch (__) {} } try { - return hljs.highlightAuto(str).value; + return hljs.highlightAuto(str).value } catch (__) {} - return ''; // use external default escaping + return '' } }) md.use(emoji) +let originalRenderToken = md.renderer.renderToken +md.renderer.renderToken = function renderToken (tokens, idx, options) { + let token = tokens[idx] + let result = originalRenderToken.call(md.renderer, tokens, idx, options) + if (token.map != null) { + return result + '' + } + return result +} + export default function markdown (content) { if (content == null) content = '' + return md.render(content.toString()) } diff --git a/lib/vars/modes.js b/lib/vars/modes.js index fe53c756..256769cf 100644 --- a/lib/vars/modes.js +++ b/lib/vars/modes.js @@ -68,7 +68,7 @@ const modes = [ { name: 'csharp', label: 'C#', - alias: ['cs'], + alias: ['cs', 'c#'], mode: 'csharp' }, { diff --git a/package.json b/package.json index 7bc93958..6a936018 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,22 @@ { "name": "boost", "version": "0.4.4", + "version": "0.4.5", "description": "Boost App", "main": "index.js", "scripts": { "start": "BOOST_ENV=development electron ./main.js", "webpack": "webpack-dev-server --hot --inline --config webpack.config.js", "compile": "NODE_ENV=production webpack --config webpack.config.production.js", - "build": "electron-packager ./ Boost --app-version=$npm_package_version $npm_package_config_platform $npm_package_config_version $npm_package_config_ignore --overwrite --asar", - "codesign": "codesign --verbose --deep --force --sign \"MAISIN solutions Inc.\" Boost-darwin-x64/Boost.app" + "pack:osx": "electron-packager ./ Boost --app-version=$npm_package_version $npm_package_config_platform $npm_package_config_version $npm_package_config_ignore --overwrite --out=\"dist\"", + "codesign": "codesign --verbose --deep --force --sign \"MAISIN solutions Inc.\" dist/Boost-darwin-x64/Boost.app", + "build:osx": "electron-builder \"dist/Boost-darwin-x64/Boost.app\" --platform=osx --out=\"dist\" --config=\"./builder-config.json\"", + "release": "electron-release --app=\"dist/Boost-darwin-x64/Boost.app\" --token=$(cat .env/.github-token) --repo=\"BoostIO/boost-releases\"" }, "config": { - "version": "--version=0.35.1 --app-bundle-id=com.maisin.boost", + "version": "--version=0.35.3 --app-bundle-id=com.maisin.boost", "platform": "--platform=darwin --arch=x64 --prune --icon=resources/app.icns", - "ignore": "--ignore=Boost-darwin-x64 --ignore=node_modules/devicon/icons --ignore=submodules/ace/(?!src-min)|submodules/ace/(?=src-min-noconflict)" + "ignore": "--ignore=.env --ignore=Boost-darwin-x64 --ignore=node_modules/devicon/icons --ignore=submodules/ace/(?!src-min)|submodules/ace/(?=src-min-noconflict)" }, "repository": { "type": "git", @@ -56,6 +59,7 @@ "css-loader": "^0.19.0", "electron-packager": "^5.1.0", "electron-prebuilt": "^0.35.1", + "electron-release": "^2.2.0", "nib": "^1.1.0", "react": "^0.14.0", "react-dom": "^0.14.0",