mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
Merge branch 'master' into fence-attrs
This commit is contained in:
@@ -14,6 +14,8 @@ import consts from 'browser/lib/consts'
|
||||
import fs from 'fs'
|
||||
const { ipcRenderer } = require('electron')
|
||||
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
|
||||
import TurndownService from 'turndown'
|
||||
import { gfm } from 'turndown-plugin-gfm'
|
||||
|
||||
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
|
||||
|
||||
@@ -57,6 +59,7 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
this.searchHandler = (e, msg) => this.handleSearch(msg)
|
||||
this.searchState = null
|
||||
this.scrollToLineHandeler = this.scrollToLine.bind(this)
|
||||
|
||||
this.formatTable = () => this.handleFormatTable()
|
||||
this.editorActivityHandler = () => this.handleEditorActivity()
|
||||
@@ -125,6 +128,7 @@ export default class CodeEditor extends React.Component {
|
||||
componentDidMount () {
|
||||
const { rulers, enableRulers } = this.props
|
||||
const expandSnippet = this.expandSnippet.bind(this)
|
||||
eventEmitter.on('line:jump', this.scrollToLineHandeler)
|
||||
|
||||
const defaultSnippet = [
|
||||
{
|
||||
@@ -475,7 +479,13 @@ export default class CodeEditor extends React.Component {
|
||||
|
||||
moveCursorTo (row, col) {}
|
||||
|
||||
scrollToLine (num) {}
|
||||
scrollToLine (event, num) {
|
||||
const cursor = {
|
||||
line: num,
|
||||
ch: 1
|
||||
}
|
||||
this.editor.setCursor(cursor)
|
||||
}
|
||||
|
||||
focus () {
|
||||
this.editor.focus()
|
||||
@@ -538,7 +548,11 @@ export default class CodeEditor extends React.Component {
|
||||
)
|
||||
return prevChar === '](' && nextChar === ')'
|
||||
}
|
||||
if (dataTransferItem.type.match('image')) {
|
||||
|
||||
const pastedHtml = clipboardData.getData('text/html')
|
||||
if (pastedHtml !== '') {
|
||||
this.handlePasteHtml(e, editor, pastedHtml)
|
||||
} else if (dataTransferItem.type.match('image')) {
|
||||
attachmentManagement.handlePastImageEvent(
|
||||
this,
|
||||
storageKey,
|
||||
@@ -608,6 +622,12 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handlePasteHtml (e, editor, pastedHtml) {
|
||||
e.preventDefault()
|
||||
const markdown = this.turndownService.turndown(pastedHtml)
|
||||
editor.replaceSelection(markdown)
|
||||
}
|
||||
|
||||
mapNormalResponse (response, pastedTxt) {
|
||||
return this.decodeResponse(response).then(body => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -64,6 +64,10 @@ class MarkdownEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
setValue (value) {
|
||||
this.refs.code.setValue(value)
|
||||
}
|
||||
|
||||
handleChange (e) {
|
||||
this.value = this.refs.code.value
|
||||
this.props.onChange(e)
|
||||
@@ -300,6 +304,7 @@ class MarkdownEditor extends React.Component {
|
||||
noteKey={noteKey}
|
||||
customCSS={config.preview.customCSS}
|
||||
allowCustomCSS={config.preview.allowCustomCSS}
|
||||
lineThroughCheckbox={config.preview.lineThroughCheckbox}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -16,7 +16,6 @@ import convertModeName from 'browser/lib/convertModeName'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import mdurl from 'mdurl'
|
||||
import exportNote from 'browser/main/lib/dataApi/exportNote'
|
||||
import { escapeHtmlCharacters } from 'browser/lib/utils'
|
||||
import context from 'browser/lib/context'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import fs from 'fs'
|
||||
@@ -80,7 +79,6 @@ function buildStyle (
|
||||
url('${appPath}/resources/fonts/MaterialIcons-Regular.woff') format('woff'),
|
||||
url('${appPath}/resources/fonts/MaterialIcons-Regular.ttf') format('truetype');
|
||||
}
|
||||
${allowCustomCSS ? customCSS : ''}
|
||||
${markdownStyle}
|
||||
|
||||
body {
|
||||
@@ -88,6 +86,11 @@ body {
|
||||
font-size: ${fontSize}px;
|
||||
${scrollPastEnd && 'padding-bottom: 90vh;'}
|
||||
}
|
||||
@media print {
|
||||
body {
|
||||
padding-bottom: initial;
|
||||
}
|
||||
}
|
||||
code {
|
||||
font-family: '${codeBlockFontFamily.join("','")}';
|
||||
background-color: rgba(0,0,0,0.04);
|
||||
@@ -144,6 +147,8 @@ body p {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
|
||||
${allowCustomCSS ? customCSS : ''}
|
||||
`
|
||||
}
|
||||
|
||||
@@ -325,9 +330,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
allowCustomCSS,
|
||||
customCSS
|
||||
)
|
||||
let body = this.markdown.render(
|
||||
escapeHtmlCharacters(noteContent, { detectCodeBlock: true })
|
||||
)
|
||||
let body = this.markdown.render(noteContent)
|
||||
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
|
||||
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(
|
||||
noteContent,
|
||||
@@ -484,10 +487,6 @@ export default class MarkdownPreview extends React.Component {
|
||||
eventEmitter.on('export:save-md', this.saveAsMdHandler)
|
||||
eventEmitter.on('export:save-html', this.saveAsHtmlHandler)
|
||||
eventEmitter.on('print', this.printHandler)
|
||||
eventEmitter.on('config-renew', () => {
|
||||
this.markdown.updateConfig()
|
||||
this.rewriteIframe()
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
@@ -531,7 +530,8 @@ export default class MarkdownPreview extends React.Component {
|
||||
prevProps.smartQuotes !== this.props.smartQuotes ||
|
||||
prevProps.sanitize !== this.props.sanitize ||
|
||||
prevProps.smartArrows !== this.props.smartArrows ||
|
||||
prevProps.breaks !== this.props.breaks
|
||||
prevProps.breaks !== this.props.breaks ||
|
||||
prevProps.lineThroughCheckbox !== this.props.lineThroughCheckbox
|
||||
) {
|
||||
this.initMarkdown()
|
||||
this.rewriteIframe()
|
||||
@@ -864,6 +864,15 @@ export default class MarkdownPreview extends React.Component {
|
||||
return
|
||||
}
|
||||
|
||||
const regexIsLine = /^:line:[0-9]/
|
||||
if (regexIsLine.test(linkHash)) {
|
||||
const numberPattern = /\d+/g
|
||||
|
||||
const lineNumber = parseInt(linkHash.match(numberPattern)[0])
|
||||
eventEmitter.emit('line:jump', lineNumber)
|
||||
return
|
||||
}
|
||||
|
||||
// this will match the old link format storage.key-note.key
|
||||
// e.g.
|
||||
// 877f99c3268608328037-1c211eb7dcb463de6490
|
||||
|
||||
@@ -20,12 +20,18 @@ class MarkdownSplitEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
setValue (value) {
|
||||
this.refs.code.setValue(value)
|
||||
}
|
||||
|
||||
handleOnChange () {
|
||||
this.value = this.refs.code.value
|
||||
this.props.onChange()
|
||||
}
|
||||
|
||||
handleScroll (e) {
|
||||
if (!this.props.config.preview.scrollSync) return
|
||||
|
||||
const previewDoc = _.get(this, 'refs.preview.refs.root.contentWindow.document')
|
||||
const codeDoc = _.get(this, 'refs.code.editor.doc')
|
||||
let srcTop, srcHeight, targetTop, targetHeight
|
||||
@@ -192,6 +198,7 @@ class MarkdownSplitEditor extends React.Component {
|
||||
noteKey={noteKey}
|
||||
customCSS={config.preview.customCSS}
|
||||
allowCustomCSS={config.preview.allowCustomCSS}
|
||||
lineThroughCheckbox={config.preview.lineThroughCheckbox}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -24,16 +24,19 @@ const TagElement = ({ tagName }) => (
|
||||
/**
|
||||
* @description Tag element list component.
|
||||
* @param {Array|null} tags
|
||||
* @param {boolean} showTagsAlphabetically
|
||||
* @return {React.Component}
|
||||
*/
|
||||
const TagElementList = tags => {
|
||||
const TagElementList = (tags, showTagsAlphabetically) => {
|
||||
if (!isArray(tags)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const tagElements = tags.map(tag => TagElement({ tagName: tag }))
|
||||
|
||||
return tagElements
|
||||
if (showTagsAlphabetically) {
|
||||
return _.sortBy(tags).map(tag => TagElement({ tagName: tag }))
|
||||
} else {
|
||||
return tags.map(tag => TagElement({ tagName: tag }))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,7 +58,8 @@ const NoteItem = ({
|
||||
pathname,
|
||||
storageName,
|
||||
folderName,
|
||||
viewType
|
||||
viewType,
|
||||
showTagsAlphabetically
|
||||
}) => (
|
||||
<div
|
||||
styleName={isActive ? 'item--active' : 'item'}
|
||||
@@ -93,7 +97,7 @@ const NoteItem = ({
|
||||
<div styleName='item-bottom'>
|
||||
<div styleName='item-bottom-tagList'>
|
||||
{note.tags.length > 0
|
||||
? TagElementList(note.tags)
|
||||
? TagElementList(note.tags, showTagsAlphabetically)
|
||||
: <span
|
||||
style={{ fontStyle: 'italic', opacity: 0.5 }}
|
||||
styleName='item-bottom-tagList-empty'
|
||||
|
||||
@@ -7,18 +7,18 @@ import styles from './StorageList.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
|
||||
/**
|
||||
* @param {Array} storgaeList
|
||||
* @param {Array} storageList
|
||||
*/
|
||||
|
||||
const StorageList = ({storageList, isFolded}) => (
|
||||
<div styleName={isFolded ? 'storageList-folded' : 'storageList'}>
|
||||
{storageList.length > 0 ? storageList : (
|
||||
<div styleName='storgaeList-empty'>No storage mount.</div>
|
||||
<div styleName='storageList-empty'>No storage mount.</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
StorageList.propTypes = {
|
||||
storgaeList: PropTypes.arrayOf(PropTypes.element).isRequired
|
||||
storageList: PropTypes.arrayOf(PropTypes.element).isRequired
|
||||
}
|
||||
export default CSSModules(StorageList, styles)
|
||||
|
||||
@@ -14,8 +14,8 @@ import CSSModules from 'browser/lib/CSSModules'
|
||||
* @param {bool} isRelated
|
||||
*/
|
||||
|
||||
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, isActive, isRelated, count}) => (
|
||||
<div styleName='tagList-itemContainer'>
|
||||
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count}) => (
|
||||
<div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
|
||||
{isRelated
|
||||
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
|
||||
<i className={isActive ? 'fa fa-minus-circle' : 'fa fa-plus-circle'} />
|
||||
@@ -25,7 +25,7 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, isAc
|
||||
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
|
||||
<span styleName='tagList-item-name'>
|
||||
{`# ${name}`}
|
||||
<span styleName='tagList-item-count'>{count}</span>
|
||||
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ import styles from './TodoListPercentage.styl'
|
||||
*/
|
||||
|
||||
const TodoListPercentage = ({
|
||||
percentageOfTodo
|
||||
percentageOfTodo, onClearCheckboxClick
|
||||
}) => (
|
||||
<div styleName='percentageBar' style={{display: isNaN(percentageOfTodo) ? 'none' : ''}}>
|
||||
<div styleName='progressBar' style={{width: `${percentageOfTodo}%`}}>
|
||||
@@ -20,11 +20,15 @@ const TodoListPercentage = ({
|
||||
<p styleName='percentageText'>{percentageOfTodo}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div styleName='todoClear'>
|
||||
<p styleName='todoClearText' onClick={(e) => onClearCheckboxClick(e)}>clear</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
TodoListPercentage.propTypes = {
|
||||
percentageOfTodo: PropTypes.number.isRequired
|
||||
percentageOfTodo: PropTypes.number.isRequired,
|
||||
onClearCheckboxClick: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default CSSModules(TodoListPercentage, styles)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.percentageBar
|
||||
display: flex
|
||||
position absolute
|
||||
top 72px
|
||||
right 0px
|
||||
@@ -30,6 +31,20 @@
|
||||
color #f4f4f4
|
||||
font-weight 600
|
||||
|
||||
.todoClear
|
||||
display flex
|
||||
justify-content: flex-end
|
||||
position absolute
|
||||
z-index 120
|
||||
width 100%
|
||||
height 100%
|
||||
padding 2px 10px
|
||||
|
||||
.todoClearText
|
||||
color #f4f4f4
|
||||
cursor pointer
|
||||
font-weight 500
|
||||
|
||||
body[data-theme="dark"]
|
||||
.percentageBar
|
||||
background-color #444444
|
||||
@@ -39,6 +54,9 @@ body[data-theme="dark"]
|
||||
|
||||
.percentageText
|
||||
color $ui-dark-text-color
|
||||
|
||||
.todoClearText
|
||||
color $ui-dark-text-color
|
||||
|
||||
body[data-theme="solarized-dark"]
|
||||
.percentageBar
|
||||
@@ -50,6 +68,9 @@ body[data-theme="solarized-dark"]
|
||||
.percentageText
|
||||
color #fdf6e3
|
||||
|
||||
.todoClearText
|
||||
color #fdf6e3
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.percentageBar
|
||||
background-color: $ui-monokai-borderColor
|
||||
@@ -69,3 +90,6 @@ body[data-theme="dracula"]
|
||||
|
||||
.percentageText
|
||||
color $ui-dracula-text-color
|
||||
|
||||
.percentageText
|
||||
color $ui-dracula-text-color
|
||||
|
||||
Reference in New Issue
Block a user