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

仕事をする上でメモを取ることはめちゃくちゃ大切だと思います。エンジニアは特にそうだと思います。
私もよくメモを取るのですが、自分好みのメモ帳が存在しないので自作することにしました。

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

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

[GitHub - BoostIO/Boostnote: A markdown editor for developers on Mac, Windows and Linux.](https://github.com/BoostIO/Boostnote)


### 成果物

<img width="1680" alt="スクリーンショット 2019-07-15 19.50.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/197668e4-b868-b645-6fe6-6c3cbbb773e1.png">

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

### とりあえず起動する

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

```
cd boostnote
```

```
yarn 
yarn run dev
```

[Boostnote/build.md at master · BoostIO/Boostnote · GitHub](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md)


環境によっては最初のyarnが少し時間かかるかもしれません。
これだけでBoostnoteが起動します。




<img width="1680" alt="スクリーンショット 2019-07-15 19.48.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/caa9a346-f794-8704-45fb-7583f8952b41.png">


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




### 事前準備

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

- [Electron \| Build cross platform desktop apps with JavaScript, HTML, and CSS.](https://electronjs.org/)
- [React – ユーザインターフェース構築のための JavaScript ライブラリ](https://ja.reactjs.org/)
- [Expressive, dynamic, robust CSS — expressive, robust, feature-rich CSS preprocessor](http://stylus-lang.com)
- [DevTools Extension \| Electron](https://electronjs.org/docs/tutorial/devtools-extension)

Reactのdevtoolがchromeに未インストールの場合は一応しときます。
electronでdev tool 使うにはなにか必要ぽっかったのですが普通に使えました。


### いじってみる

以下、個人的に気に入らない部分をいじっていきます。
なんか変更したら更新していくと思います。

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

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

<img width="1680" alt="スクリーンショット 2019-07-15 19.47.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/e815ad6a-fd65-8c41-5376-a69575f4bfba.png">

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


``` js
  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%使用しないのでごっそり削ります。

``` js
  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

<img width="236" alt="スクリーンショット 2019-07-16 9.09.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/294b0a30-57cc-c0ad-e2eb-83e9e653c892.png">

After

<img width="220" alt="スクリーンショット 2019-07-16 9.09.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/cdb31721-f5e6-ef30-24b6-2bf35d326d73.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という部分を発見。
どうやら設定でかえられる？と思いつつ見てみると、タグ毎に色をつけられるらしい。


``` js
        <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

``` js
  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とかはてブとかのの記事作成画面のように、タイトルはタイトル入力用のボックスに入力したい。


<img width="1680" alt="スクリーンショット 2019-07-15 19.50.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/197668e4-b868-b645-6fe6-6c3cbbb773e1.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`とあってこれも設定で変えられそうに思いきや、追随を無効にしてくれるだけですね。アニメーションを無効にしたいのでやっぱりソースイジる必要がありました。

``` .js
  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 undefineｄ`と出てくる。
これで検索するとgrup4とかでてくるけど、つかってなさそうだし。
そもそもローカルでもbuildできないように制限されてるのかな？そんなことはないと思うけど。

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

### 感想

- git clone してすぐに起動できるが素晴らしすぎる。
- ソースコードの質の高さは判断できないがが、シンプルに実装されていて読みやすい
- 今回いじった部分ではファイルの行数がそこまで多くなかった。
- かなり素直に実装されていて、React触ったことなくてもなんとなく処理を追うことができた。
- Javascriptの文法も若干見たことがなかったものがあったり、ちょっぴり楽しかった。
- アプリとか作るの好きな人はソース追ってて楽しいと思う。


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

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