mirror of
https://github.com/BoostIo/Boostnote
synced 2026-05-15 20:11:53 +00:00
Import note from url with markdown
This commit is contained in:
69
browser/main/Detail/FromUrlButton.js
Normal file
69
browser/main/Detail/FromUrlButton.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import React from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './FromUrlButton.styl'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import i18n from 'browser/lib/i18n'
|
||||||
|
|
||||||
|
class FromUrlButton extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isActive: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseDown (e) {
|
||||||
|
this.setState({
|
||||||
|
isActive: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseUp (e) {
|
||||||
|
this.setState({
|
||||||
|
isActive: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseLeave (e) {
|
||||||
|
this.setState({
|
||||||
|
isActive: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { className } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className={_.isString(className)
|
||||||
|
? 'FromUrlButton ' + className
|
||||||
|
: 'FromUrlButton'
|
||||||
|
}
|
||||||
|
styleName={this.state.isActive || this.props.isActive
|
||||||
|
? 'root--active'
|
||||||
|
: 'root'
|
||||||
|
}
|
||||||
|
onMouseDown={(e) => this.handleMouseDown(e)}
|
||||||
|
onMouseUp={(e) => this.handleMouseUp(e)}
|
||||||
|
onMouseLeave={(e) => this.handleMouseLeave(e)}
|
||||||
|
onClick={this.props.onClick}>
|
||||||
|
<img styleName='icon'
|
||||||
|
src={this.state.isActive || this.props.isActive
|
||||||
|
? '../resources/icon/icon-external.svg'
|
||||||
|
: '../resources/icon/icon-external.svg'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span styleName='tooltip'>{i18n.__('Convert URL to Markdown')}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FromUrlButton.propTypes = {
|
||||||
|
isActive: PropTypes.bool,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
className: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(FromUrlButton, styles)
|
||||||
41
browser/main/Detail/FromUrlButton.styl
Normal file
41
browser/main/Detail/FromUrlButton.styl
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
.root
|
||||||
|
top 45px
|
||||||
|
topBarButtonRight()
|
||||||
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
color alpha($ui-favorite-star-button-color, 0.6)
|
||||||
|
&:hover .tooltip
|
||||||
|
opacity 1
|
||||||
|
|
||||||
|
.tooltip
|
||||||
|
tooltip()
|
||||||
|
position absolute
|
||||||
|
pointer-events none
|
||||||
|
top 50px
|
||||||
|
right 125px
|
||||||
|
width 90px
|
||||||
|
z-index 200
|
||||||
|
padding 5px
|
||||||
|
line-height normal
|
||||||
|
border-radius 2px
|
||||||
|
opacity 0
|
||||||
|
transition 0.1s
|
||||||
|
|
||||||
|
.root--active
|
||||||
|
@extend .root
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
color alpha($ui-favorite-star-button-color, 0.6)
|
||||||
|
|
||||||
|
.icon
|
||||||
|
transition transform 0.15s
|
||||||
|
height 13px
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.root
|
||||||
|
topBarButtonDark()
|
||||||
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
color alpha($ui-favorite-star-button-color, 0.6)
|
||||||
@@ -368,6 +368,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div styleName='info-right'>
|
<div styleName='info-right'>
|
||||||
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />
|
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />
|
||||||
|
|
||||||
<StarButton
|
<StarButton
|
||||||
onClick={(e) => this.handleStarButtonClick(e)}
|
onClick={(e) => this.handleStarButtonClick(e)}
|
||||||
isActive={note.isStarred}
|
isActive={note.isStarred}
|
||||||
|
|||||||
@@ -37,4 +37,4 @@ body[data-theme="dark"]
|
|||||||
topBarButtonDark()
|
topBarButtonDark()
|
||||||
&:hover
|
&:hover
|
||||||
transition 0.2s
|
transition 0.2s
|
||||||
color alpha($ui-favorite-star-button-color, 0.6)
|
color alpha($ui-favorite-star-button-color, 0.6)
|
||||||
|
|||||||
184
browser/main/modals/CreateMarkdownFromURLModal.js
Normal file
184
browser/main/modals/CreateMarkdownFromURLModal.js
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import React from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './CreateMarkdownFromURLModal.styl'
|
||||||
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
|
import store from 'browser/main/store'
|
||||||
|
import consts from 'browser/lib/consts'
|
||||||
|
import ModalEscButton from 'browser/components/ModalEscButton'
|
||||||
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
import i18n from 'browser/lib/i18n'
|
||||||
|
const http = require('http')
|
||||||
|
const https = require('https')
|
||||||
|
const TurndownService = require('turndown')
|
||||||
|
import { hashHistory } from 'react-router'
|
||||||
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
|
class CreateMarkdownFromURLModal extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
let td = new TurndownService();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
name: '',
|
||||||
|
showerror: false,
|
||||||
|
errormessage: '',
|
||||||
|
turndownService: td
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
this.refs.name.focus()
|
||||||
|
this.refs.name.select()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCloseButtonClick (e) {
|
||||||
|
this.props.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange (e) {
|
||||||
|
this.setState({
|
||||||
|
name: this.refs.name.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown (e) {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
this.props.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInputKeyDown (e) {
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case 13:
|
||||||
|
this.confirm()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirmButtonClick (e) {
|
||||||
|
this.confirm()
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(message) {
|
||||||
|
this.setState({
|
||||||
|
showerror: true,
|
||||||
|
errormessage: message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hideError() {
|
||||||
|
this.setState({
|
||||||
|
showerror: false,
|
||||||
|
errormessage: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
validateUrl(str) {
|
||||||
|
if(/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(str)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm () {
|
||||||
|
if(this.validateUrl(this.state.name)) {
|
||||||
|
this.hideError()
|
||||||
|
let url = this.state.name;
|
||||||
|
let request = http;
|
||||||
|
if(url.includes('https'))
|
||||||
|
request = https;
|
||||||
|
let req = request.request(url, (res) => {
|
||||||
|
let data = '';
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk
|
||||||
|
})
|
||||||
|
res.on('end', () => {
|
||||||
|
console.log("receiving data", data);
|
||||||
|
|
||||||
|
let html = document.createElement('html');
|
||||||
|
html.innerHTML = data;
|
||||||
|
|
||||||
|
let scripts = html.getElementsByTagName('script');
|
||||||
|
for(let i = scripts.length - 1; i >= 0; i--) {
|
||||||
|
scripts[i].parentNode.removeChild(scripts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = html.getElementsByTagName('body')[0].innerHTML;
|
||||||
|
let markdownHTML = this.state.turndownService.turndown(body);
|
||||||
|
console.log('markdown', markdownHTML);
|
||||||
|
html.innerHTML = '';
|
||||||
|
const { storage, folder, dispatch, location } = this.props
|
||||||
|
|
||||||
|
dataApi
|
||||||
|
.createNote(storage, {
|
||||||
|
type: 'MARKDOWN_NOTE',
|
||||||
|
folder: folder,
|
||||||
|
title: '',
|
||||||
|
content: markdownHTML
|
||||||
|
})
|
||||||
|
.then((note) => {
|
||||||
|
const noteHash = note.key
|
||||||
|
dispatch({
|
||||||
|
type: 'UPDATE_NOTE',
|
||||||
|
note: note
|
||||||
|
})
|
||||||
|
hashHistory.push({
|
||||||
|
pathname: location.pathname,
|
||||||
|
query: {key: noteHash}
|
||||||
|
})
|
||||||
|
ee.emit('list:jump', noteHash)
|
||||||
|
ee.emit('detail:focus')
|
||||||
|
this.props.close()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
req.on('error', (e) => {
|
||||||
|
console.log("Error in request", e.message);
|
||||||
|
});
|
||||||
|
req.end();
|
||||||
|
} else {
|
||||||
|
this.showError("Please check your URL is in correct format. (Example, 'https://google.com')")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div styleName='root'
|
||||||
|
tabIndex='-1'
|
||||||
|
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||||
|
>
|
||||||
|
<div styleName='header'>
|
||||||
|
<div styleName='title'>{i18n.__('Import Markdown From URL')}</div>
|
||||||
|
</div>
|
||||||
|
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
|
||||||
|
<div styleName='control'>
|
||||||
|
<div styleName='control-folder'>
|
||||||
|
<div styleName='control-folder-label'>{i18n.__('Insert URL Here')}</div>
|
||||||
|
<input styleName='control-folder-input'
|
||||||
|
ref='name'
|
||||||
|
value={this.state.name}
|
||||||
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
onKeyDown={(e) => this.handleInputKeyDown(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button styleName='control-confirmButton'
|
||||||
|
onClick={(e) => this.handleConfirmButtonClick(e)}
|
||||||
|
>
|
||||||
|
{i18n.__('Import')}
|
||||||
|
</button>
|
||||||
|
<div className="error" styleName="error">{this.state.errormessage}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateMarkdownFromURLModal.propTypes = {
|
||||||
|
storage: PropTypes.shape({
|
||||||
|
key: PropTypes.string
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(CreateMarkdownFromURLModal, styles)
|
||||||
134
browser/main/modals/CreateMarkdownFromURLModal.styl
Normal file
134
browser/main/modals/CreateMarkdownFromURLModal.styl
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
.root
|
||||||
|
modal()
|
||||||
|
width 500px
|
||||||
|
height 270px
|
||||||
|
overflow hidden
|
||||||
|
position relative
|
||||||
|
|
||||||
|
.header
|
||||||
|
height 80px
|
||||||
|
margin-bottom 10px
|
||||||
|
margin-top 20px
|
||||||
|
font-size 18px
|
||||||
|
line-height 50px
|
||||||
|
background-color $ui-backgroundColor
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.title
|
||||||
|
font-size 36px
|
||||||
|
font-weight 600
|
||||||
|
|
||||||
|
.control-folder-label
|
||||||
|
text-align left
|
||||||
|
font-size 14px
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.control-folder-input
|
||||||
|
display block
|
||||||
|
height 40px
|
||||||
|
width 490px
|
||||||
|
padding 0 5px
|
||||||
|
margin 10px 0
|
||||||
|
border 1px solid $ui-input--create-folder-modal
|
||||||
|
border-radius 2px
|
||||||
|
background-color transparent
|
||||||
|
outline none
|
||||||
|
vertical-align middle
|
||||||
|
font-size 16px
|
||||||
|
&:disabled
|
||||||
|
background-color $ui-input--disabled-backgroundColor
|
||||||
|
&:focus, &:active
|
||||||
|
border-color $ui-active-color
|
||||||
|
|
||||||
|
.control-confirmButton
|
||||||
|
display block
|
||||||
|
height 35px
|
||||||
|
width 140px
|
||||||
|
border none
|
||||||
|
border-radius 2px
|
||||||
|
padding 0 25px
|
||||||
|
margin 20px auto
|
||||||
|
font-size 14px
|
||||||
|
colorPrimaryButton()
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.root
|
||||||
|
modalDark()
|
||||||
|
width 500px
|
||||||
|
height 270px
|
||||||
|
overflow hidden
|
||||||
|
position relative
|
||||||
|
|
||||||
|
.header
|
||||||
|
background-color transparent
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.control-folder-label
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.control-folder-input
|
||||||
|
border 1px solid $ui-input--create-folder-modal
|
||||||
|
color white
|
||||||
|
|
||||||
|
.description
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.control-confirmButton
|
||||||
|
colorDarkPrimaryButton()
|
||||||
|
|
||||||
|
body[data-theme="solarized-dark"]
|
||||||
|
.root
|
||||||
|
modalSolarizedDark()
|
||||||
|
width 500px
|
||||||
|
height 270px
|
||||||
|
overflow hidden
|
||||||
|
position relative
|
||||||
|
|
||||||
|
.header
|
||||||
|
background-color transparent
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
color $ui-solarized-dark-text-color
|
||||||
|
|
||||||
|
.control-folder-label
|
||||||
|
color $ui-solarized-dark-text-color
|
||||||
|
|
||||||
|
.control-folder-input
|
||||||
|
border 1px solid $ui-input--create-folder-modal
|
||||||
|
color white
|
||||||
|
|
||||||
|
.description
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.control-confirmButton
|
||||||
|
colorSolarizedDarkPrimaryButton()
|
||||||
|
|
||||||
|
.error
|
||||||
|
text-align center
|
||||||
|
color #F44336
|
||||||
|
|
||||||
|
body[data-theme="monokai"]
|
||||||
|
.root
|
||||||
|
modalMonokai()
|
||||||
|
width 500px
|
||||||
|
height 270px
|
||||||
|
overflow hidden
|
||||||
|
position relative
|
||||||
|
|
||||||
|
.header
|
||||||
|
background-color transparent
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
color $ui-monokai-text-color
|
||||||
|
|
||||||
|
.control-folder-label
|
||||||
|
color $ui-monokai-text-color
|
||||||
|
|
||||||
|
.control-folder-input
|
||||||
|
border 1px solid $ui-input--create-folder-modal
|
||||||
|
color white
|
||||||
|
|
||||||
|
.description
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.control-confirmButton
|
||||||
|
colorMonokaiPrimaryButton()
|
||||||
@@ -7,6 +7,8 @@ import ee from 'browser/main/lib/eventEmitter'
|
|||||||
import ModalEscButton from 'browser/components/ModalEscButton'
|
import ModalEscButton from 'browser/components/ModalEscButton'
|
||||||
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
import i18n from 'browser/lib/i18n'
|
import i18n from 'browser/lib/i18n'
|
||||||
|
import { openModal } from 'browser/main/lib/modal'
|
||||||
|
import CreateMarkdownFromURLModal from '../modals/CreateMarkdownFromURLModal'
|
||||||
|
|
||||||
class NewNoteModal extends React.Component {
|
class NewNoteModal extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -24,6 +26,18 @@ class NewNoteModal extends React.Component {
|
|||||||
this.props.close()
|
this.props.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCreateMarkdownFromUrlClick(e) {
|
||||||
|
this.props.close()
|
||||||
|
|
||||||
|
const { storage, folder, dispatch, location } = this.props
|
||||||
|
openModal(CreateMarkdownFromURLModal, {
|
||||||
|
storage: storage,
|
||||||
|
folder: folder,
|
||||||
|
dispatch,
|
||||||
|
location
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleMarkdownNoteButtonClick (e) {
|
handleMarkdownNoteButtonClick (e) {
|
||||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_MARKDOWN')
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_MARKDOWN')
|
||||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
|
||||||
@@ -142,6 +156,7 @@ class NewNoteModal extends React.Component {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div styleName='description'><i className='fa fa-arrows-h' />{i18n.__('Tab to switch format')}</div>
|
<div styleName='description'><i className='fa fa-arrows-h' />{i18n.__('Tab to switch format')}</div>
|
||||||
|
<div styleName='from-url' onClick={(e) => this.handleCreateMarkdownFromUrlClick(e)}>Or, create a new markdown note from a URL</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -47,6 +47,12 @@
|
|||||||
text-align center
|
text-align center
|
||||||
margin-bottom 25px
|
margin-bottom 25px
|
||||||
|
|
||||||
|
.from-url
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
text-align center
|
||||||
|
margin-bottom 25px
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
modalDark()
|
modalDark()
|
||||||
@@ -61,7 +67,7 @@ body[data-theme="dark"]
|
|||||||
&:focus
|
&:focus
|
||||||
colorDarkPrimaryButton()
|
colorDarkPrimaryButton()
|
||||||
|
|
||||||
.description
|
.description, .from-url
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
body[data-theme="solarized-dark"]
|
body[data-theme="solarized-dark"]
|
||||||
@@ -78,7 +84,7 @@ body[data-theme="solarized-dark"]
|
|||||||
&:focus
|
&:focus
|
||||||
colorDarkPrimaryButton()
|
colorDarkPrimaryButton()
|
||||||
|
|
||||||
.description
|
.description, .from-url
|
||||||
color $ui-solarized-dark-text-color
|
color $ui-solarized-dark-text-color
|
||||||
|
|
||||||
body[data-theme="monokai"]
|
body[data-theme="monokai"]
|
||||||
@@ -95,5 +101,5 @@ body[data-theme="monokai"]
|
|||||||
&:focus
|
&:focus
|
||||||
colorDarkPrimaryButton()
|
colorDarkPrimaryButton()
|
||||||
|
|
||||||
.description
|
.description, .from-url
|
||||||
color $ui-monokai-text-color
|
color $ui-monokai-text-color
|
||||||
|
|||||||
17485
package-lock.json
generated
17485
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -96,6 +96,7 @@
|
|||||||
"striptags": "^2.2.1",
|
"striptags": "^2.2.1",
|
||||||
"superagent": "^1.2.0",
|
"superagent": "^1.2.0",
|
||||||
"superagent-promise": "^1.0.3",
|
"superagent-promise": "^1.0.3",
|
||||||
|
"turndown": "^4.0.2",
|
||||||
"unique-slug": "2.0.0",
|
"unique-slug": "2.0.0",
|
||||||
"uuid": "^3.2.1"
|
"uuid": "^3.2.1"
|
||||||
},
|
},
|
||||||
|
|||||||
39
resources/icon/icon-external.svg
Normal file
39
resources/icon/icon-external.svg
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<title>icon-external</title>
|
||||||
|
<defs>
|
||||||
|
<filter x="0.0%" y="0.0%" width="100.0%" height="100.0%" filterUnits="objectBoundingBox" id="filter-1">
|
||||||
|
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowOffsetOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||||
|
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<g id="Artboard-4" stroke="none" stroke-width="1" fill="#c7c7c7" fill-rule="evenodd" transform="" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M412.88,261.464c-11.423,0-20.682,9.259-20.682,20.682v156.879c0,17.43-14.181,31.611-31.612,31.611H72.975
|
||||||
|
c-17.43,0-31.611-14.181-31.611-31.611V151.414c0-17.43,14.181-31.611,31.611-31.611h156.879c11.422,0,20.682-9.26,20.682-20.682
|
||||||
|
c0-11.422-9.26-20.682-20.682-20.682H72.975C32.737,78.439,0,111.176,0,151.414v287.611C0,479.264,32.737,512,72.975,512h287.61
|
||||||
|
c40.239,0,72.976-32.736,72.977-72.975V282.146C433.562,270.723,424.303,261.464,412.88,261.464z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M491.318,0H334.439c-11.423,0-20.682,9.26-20.682,20.682c0,11.422,9.259,20.682,20.682,20.682h136.197v136.197
|
||||||
|
c0,11.422,9.259,20.682,20.682,20.682c11.423,0,20.682-9.26,20.682-20.682V20.682C512,9.26,502.741,0,491.318,0z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M505.942,6.058c-8.077-8.076-21.172-8.076-29.249,0L189.082,293.668c-8.077,8.077-8.077,21.172,0,29.249
|
||||||
|
c4.038,4.039,9.332,6.058,14.625,6.058c5.294,0,10.587-2.02,14.625-6.058L505.942,35.307
|
||||||
|
C514.019,27.23,514.019,14.135,505.942,6.058z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
Reference in New Issue
Block a user