Edited at

Boostnoteをcloneして自分好みのMarkdownメモアプリを作成する

仕事をする上でメモを取ることはめちゃくちゃ大切だと思います。エンジニアは特にそうだと思います。

私もよくメモを取るのですが、自分好みのメモ帳が存在しないので自作することにしました。

electron + vue.js でプロトタイプを作っていたのですが、調べていたとこ、boostnoteがOSSとして公開されていることを知りました。2年ほど前使っていてかなり良かったのですが、ちょっと気に入らない点があってやめてしました。

今回はOSSとして公開されているBoostnoteを引っ張ってきて、カスタマイズしていこうと思います。

GitHub - BoostIO/Boostnote: A markdown editor for developers on Mac, Windows and Linux.


成果物

スクリーンショット 2019-07-15 19.50.07.png

写真だと上にタイトル入力画面追加したくらいしかわからないですね


とりあえず起動する

git clone git@github.com:BoostIO/Boostnote.git

cd boostnote

yarn 

yarn run dev

Boostnote/build.md at master · BoostIO/Boostnote · GitHub

環境によっては最初のyarnが少し時間かかるかもしれません。

これだけでBoostnoteが起動します。

スクリーンショット 2019-07-15 19.48.14.png

個人的にはGUIの操作がないぶん普通にインストールする楽なきがします


事前準備

特にないです。適当にドキュメント眺める程度です。

Reactのdevtoolがchromeに未インストールの場合は一応しときます。

electronでdev tool 使うにはなにか必要ぽっかったのですが普通に使えました。


いじってみる

以下、個人的に気に入らない部分をいじっていきます。

なんか変更したら更新していくと思います。


新規作成時にMarkdonw Note or Snippet Noteのモーダルが出てくる

Boostnoteのsnippet機能は使わないので、新規作成ですぐにマークダウンを生成してほしいので写真のモーダルはいらない。

スクリーンショット 2019-07-15 19.47.41.png

この程度なら設定で変えられそうと思いながらソースを追っていったら

  handleNewNoteButtonClick (e) {

const { location, dispatch, match: { params }, config } = this.props
const { storage, folder } = this.resolveTargetFolder()
if (config.ui.defaultNote === 'MARKDOWN_NOTE') {
createMarkdownNote(storage.key, folder.key, dispatch, location, params, config)
} else if (config.ui.defaultNote === 'SNIPPET_NOTE') {
createSnippetNote(storage.key, folder.key, dispatch, location, params, config)
} else {
modal.open(NewNoteModal, {
storage: storage.key,
folder: folder.key,
dispatch,
location,
params,
config
})
}

configで条件分岐していました。設定でオフにできそうです。

と思ったけど、100%使用しないのでごっそり削ります。

  handleNewNoteButtonClick (e) {

const { location, dispatch, match: { params }, config } = this.props
const { storage, folder } = this.resolveTargetFolder()
createMarkdownNote(storage.key, folder.key, dispatch, location, params, config)
}

https://github.com/BoostIO/Boostnote/blob/master/browser/main/NewNoteButton/index.js#L35#L52


タグのスタイルが気に食わない

Before

スクリーンショット 2019-07-16 9.09.31.png

After

スクリーンショット 2019-07-16 9.09.24.png

個人的にはタグの頭の#が不要で、あと色も薄いので変えたい。

頭の#に関しては表示場所で追加してるだけなので、適当に目星をつけてgrepして直ぐに発見。

xxxx ~/w/Boostnote> git grep "#{tag"

browser/components/NoteItem.js: #{tagName}
browser/main/Detail/TagSelect.js: <span styleName='tag-label' style={textStyle} onClick={(e) => this.handleTagLabelClick(tag)}>#{tag}</span>

出てきた箇所の#を消します。

ついでに色を変更したいと思いみてみると、config.coloredTagsという部分を発見。

どうやら設定でかえられる?と思いつつ見てみると、タグ毎に色をつけられるらしい。

        <TagSelect

ref='tags'
value={this.state.note.tags}
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
showTagsAlphabetically={config.ui.showTagsAlphabetically}
data={data}
onChange={this.handleUpdateTag.bind(this)}
coloredTags={config.coloredTags}
/>

なるほど・・

タグの生成時に自動で自分の好きな色を追加してしまおうと思ったが

そもそもタグに色つけないので、デフォルトのテーマカラーを変えました。

https://github.com/churabou/Boostnote/commit/0d8c3529ed13db6b20b61a36d4eb81a6c2584f85

  handleColorPickerConfirm (color) {

const { dispatch, config: {coloredTags} } = this.props
const { colorPicker: { tagName } } = this.state
const newColoredTags = Object.assign({}, coloredTags, {[tagName]: color.hex})

const config = { coloredTags: newColoredTags }
ConfigManager.set(config)
dispatch({
type: 'SET_CONFIG',
config
})
this.dismissColorPicker()
}


1行目がタイトルになるけど、タイトルは別に入力したい。

Qiitaとかはてブとかのの記事作成画面のように、タイトルはタイトル入力用のボックスに入力したい。

スクリーンショット 2019-07-15 19.50.07.png

https://github.com/churabou/Boostnote/commit/4a1b835f3cd687efd73769b3b3949ac5de9685f2


入力画面をスクロールするとプレビューも追随してスクロールするのがうざい。

入力画面をスクロールすると追随してPreview画面もスクロールしてくれるのですが、移動にアニメーションあってすごい鬱陶しいです。

これは親切な機能なのですがすごくもどかしい。アニメーションを無効にしたいと思います。その点Qiitaの記事作成画面は自然に追随してくれて良いですね!

https://github.com/BoostIO/Boostnote/blob/master/browser/components/MarkdownSplitEditor.js#L32L75

このあたり、なんかめっちゃスクロールしてそうです。this.props.config.preview.scrollSyncとあってこれも設定で変えられそうに思いきや、追随を無効にしてくれるだけですね。アニメーションを無効にしたいのでやっぱりソースイジる必要がありました。

  handleScroll (e) {

if (!this.props.config.preview.scrollSync) return
console.log('123445')
const previewDoc = _.get(this, 'refs.preview.refs.root.contentWindow.document')
const codeDoc = _.get(this, 'refs.code.editor.doc')
let srcTop, srcHeight, targetTop, targetHeight

if (this.userScroll) {
if (e.doc) {
srcTop = _.get(e, 'doc.scrollTop')
srcHeight = _.get(e, 'doc.height')
targetTop = _.get(previewDoc, 'body.scrollTop')
targetHeight = _.get(previewDoc, 'body.scrollHeight')
} else {
srcTop = _.get(previewDoc, 'body.scrollTop')
srcHeight = _.get(previewDoc, 'body.scrollHeight')
targetTop = _.get(codeDoc, 'scrollTop')
targetHeight = _.get(codeDoc, 'height')
}

const distance = (targetHeight * srcTop / srcHeight) - targetTop
const framerate = 1000 / 60
const frames = 20
const refractory = frames * framerate

this.userScroll = false

let frame = 0
let scrollPos, time
const timer = setInterval(() => {
time = frame / frames
scrollPos = time < 0.5
? 2 * time * time // ease in
: -1 + (4 - 2 * time) * time // ease out
if (e.doc) _.set(previewDoc, 'body.scrollTop', targetTop + scrollPos * distance)
else _.get(this, 'refs.code.editor').scrollTo(0, targetTop + scrollPos * distance)
if (frame >= frames) {
clearInterval(timer)
setTimeout(() => { this.userScroll = true }, refractory)
}
frame++
}, framerate)
}
}

下の方を少しだけ変更してアニメーションを無効にします。


ショートカットを追加する


  • 時間がある時に更新


Buildする (Help me)

grunt pre-build

をすると/distにboostnoteが生成される。

ただこれを起動するとロード画面のまま起動せず、devtoolを見てみると、

Uncaught TypeError: Cannot read property 'apply' of undefinedと出てくる。

これで検索するとgrup4とかでてくるけど、つかってなさそうだし。

そもそもローカルでもbuildできないように制限されてるのかな?そんなことはないと思うけど。

どなたかわかる方、コメントよろしくおねがいします。


感想


  • git clone してすぐに起動できるが素晴らしすぎる。

  • ソースコードの質の高さは判断できないがが、シンプルに実装されていて読みやすい

  • 今回いじった部分ではファイルの行数がそこまで多くなかった。

  • かなり素直に実装されていて、React触ったことなくてもなんとなく処理を追うことができた。

  • Javascriptの文法も若干見たことがなかったものがあったり、ちょっぴり楽しかった。

  • アプリとか作るの好きな人はソース追ってて楽しいと思う。


React勉強したい人は絶対cloneしてソース追ってみるべき

これだけのアプリのソースが無料で公開されているのでそう思いました。