Electron製のアプリの起動を速くするelectron-linkの話

最近、社内でLT(https://labs.gree.jp/blog/2018/10/17291/)があって、そこで話をしたので、その時の内容を書く。
これは、その一部の話。

経緯とか

どうにかして、アプリを速くできないかなー、って情報探してたら
Atomというエディタの起動時間を高速化した時の情報が載ってていたのであった。

http://blog.atom.io/2017/04/18/improving-startup-time.html

Atomは他にもいろいろやって、最終的に50%速くなった、と。

electron-link

https://github.com/atom/electron-link

  • Atomのビルドツール。ライブラリを遅延ロードするコードに変換する。(試してない)
  • でも、使わなくても良いはず。変換結果は分かっているので、手動で書き換えられますね。
    • (実際に使って試してないから、実は変換方式が違っていたらどうしよう。)

書き換え

var log = require('electron-log');

log.error('error found.');

これ(↑)をこんな感じ(↓)書き換えるのじゃ。

var _log, log = () => { _log = _log || require('electron-log'); return _log; };
 
log().error('error found.');

書き換えの成果

  • 手動での書き換えは、書き換え箇所が多くて、それなりに面倒だった。
  • 自分の管理するコードはずぼら実装だったので、20%ぐらい起動速度が速くなった。
    • (BrowserWindowが立ち上がってから、表示されるまでの時間の範囲)
    • (大半はレンダリング時間)

使っているAltJSやフレームワークに遅延ローディングの仕組みがあったら、
そっち使ってもイイヨネ。

ElectronのBrowserWindow間で直接メッセージをやりとりする

最近、社内でLT(https://labs.gree.jp/blog/2018/10/17291/)があって、そこで話をしたので、その時の内容を書く。
これは、その一部の話。

経緯とか要約とか

複数立ち上げたBrowserWindowのインスタンス間でメッセージをやりとりする。
社内のメモで出来ない。いったん、main processを経由しないといけない、と書いている人が居たので、
ちょっと書いたのであった。

説明

ウィンドウ間でメッセージをやりとりするには、
main processをいったん経由してから、
他のrenderer processにメッセージを送るのが作法のような気がするが


どうにかして、BrowserWindowのインスタンスを取得すれば、
各ウィンドウ間で直接データをやりとりできる。

具体的な実装の例

// https://github.com/taku-o/hello-electron-003-to-sub-msg/blob/master/electron.ts
mainWindow = new BrowserWindow({
    width: 600,
    height: 600,
    acceptFirstMouse: true,
    show: true
});
mainWindow.loadURL(`file://${__dirname}/main.html`);
// !!!!
global.mainWindow = mainWindow;
  • メッセージを投げる側の例
// https://github.com/taku-o/hello-electron-003-to-sub-msg/blob/master/js/main.ts
$scope.sendMsgTo1stWindow = function() {
    // remoteは参照そのものではなく、呼んだ瞬間のコピーのような
    const firstWindow = require(‘electron').remote.getGlobal('firstWindow');                  
    firstWindow.webContents.send('message', 'message from main.');
};
  • メッセージを受ける側の例
// https://github.com/taku-o/hello-electron-003-to-sub-msg/blob/master/js/second.ts
var ipcRenderer = require('electron').ipcRenderer;

// recieve message
$scope.message = '';
ipcRenderer.on('message', (event, message: string) => {
    $scope.message = message;
    $timeout(() => { $scope.$apply(); });
});

こんなに短いサンプルコードなのに、angularやtypescript使っていてわかりにくいとか、
global使うのは反則ではないのか?
とかあるかもしれないけど、あまり細かいことは気にしないで欲しい。

"できる"と"やる"は別の話

でも、結局は、main processで、BrowserWindowのインスタンスを管理することになると思います。
だいたい設計的な理由で。その方がいろいろ利点があるはず。

  • BrowserWindowのインスタンスを管理する場所を散らばらせるのは良くない
  • メニューとかにBrowserWindowのインスタンスを渡せると、処理上、都合の良い場合が多い。

サンプルコード

今回の実験のために作ったサンプルコードです。

Electron Fiddle

最近、社内でLT(https://labs.gree.jp/blog/2018/10/17291/)があって、そこで話をしたので、その時の内容を書く。
これは、その一部のElectron Fiddleの話。

Electron Fiddle

Electronコードの挙動を簡易に確認できるアプリ。共有用のサンプルコードをGistに上げられるアプリ。
実行するElectronのバージョンも指定できる。
最近できたプロジェクトで、アプリはもう動く。開発は活発な方。
JavaScriptの挙動をデモできるJSFiddleというサービスがあるが、アレに似ている。(https://jsfiddle.net)

Good

  • 最小のアプリを作るなら、electron-quick-start より早い
  • ワンクリックで必要なものが、じゃかじゃかダウンロードされて、アプリが出来上がる。

うーん

  • 今のところ、main process (.js)と、renderer process (.js, .html)の3つのファイルを使ったサンプルコードしか作れない。
  • npmのモジュールも使えない

renderer processは、リロードして再読み込みすれば済むって話はあるので、
main processのコードだけ動的に書き換えられれば、実はそれで事足りるのか?
リロードすれば済むのは、JSFiddleだってそうだしね。

npm module を使う

今のところ、Electron Fiddleでは、npmのモジュールは使えないが、(そのうち対応されるだろう)
ファイルをはき出したあと、

npm install
npm install module-name
npm start

とすれば、Electron Fiddleの上ではないけれど、npmのモジュールを利用できる。できた。

build Electron Fiddle source

GitHubソースコードは↓とすれば、ビルドできた。
これでいろいろ弄れそう。

git clone git@github.com:electron/fiddle.git
cd fiddle
npm install

npm run make
nmp run start

[情報技術][mac] 更新のたびに早くなるParallels Desktopが宣伝文句上、どれくらい早くなったか

メーラーを確認したら、Parallels Desktopの広告メールが今回もまたやってきた。

なんでも、新しくParallels Desktop 14が出るそうな。

https://www.parallels.com/jp/



このParallels DesktopMac上でWindowsなどを動作させたり出来る便利なアプリなのですけれど、

メモリをふんだんに足しさえすれば、非常に軽く動作してくれるので、普段からとても愛用させて頂いている。



ところで、このParallels Desktop、少し気になることがある。

気のせいか、バージョンアップのたびに、

毎回毎回"パフォーマンスがn%改善した"と書いてあるような気がするのです。



もし、それが本当なら、最新バージョンは14。

何度も何度も更新が積み重なって、凄まじい性能改善である。



ということで、調べられる範囲で各バージョンの宣伝文句を調査してみた。


Parallels Desktop 7 for MacParallels Desktop 8 for Mac

パフォーマンスに関して記載した資料が見つからない。残念だ。


Parallels Desktop 9 for Mac

記載が細かい。ここからParallels Desktopの高速化が始まる。

ディスクパフォーマンスを40%改善、
仮想マシンのシャットダウン速度が最大25%速く、
仮想マシンサスペンドが最大20%速く、
3DグラフィクスとWebブラウジングが15%高速に。

Parallels Desktop 10 for Mac

バージョンによって、少しずつ書き方が違うことに今更気づく。

Windows でドキュメントを開く際に 48% も速く
「Office」アプリケーションの起動を最大50%高速化

Parallels Desktop 11 for Mac

50%高速化。すごい。

以前のバージョンよりも 50% 高速

Parallels Desktop 12 for Mac

25%。これでも充分すごい。

Windowsのパフォーマンスが25%高速化

Parallels Desktop 13 for Mac

ファイルへのアクセス限定での高速化記載になっていた。

Windows のファイルとドキュメントへのアクセスが 47% 高速化

Parallels Desktop 14 for Mac

"Windows およびアプリケーションの起動速度が最大 35% 改善。"とメールに記載があるが、

これはきっと間違いで、比較表では起動速度の高速化は最大3倍と書いてある。

速度を35%高速化

まとめ・感想

Parallels Desktopのバージョンアップのたびに送信されていたメールでは、常に全体的に高速化しているわけではなく、

バージョンアップのたびに、限定された範囲での高速化をうたっていたようです。

流し読みしていたので、今回まとめるまでまるで気づきませんでしたマル。

(でも、記載していない部分が高速化されていないわけではないみたいだよ)



体感では、マシンを新調したりしたり、メモリの割り当てを増やしたりしたので、

どれくらい早くなっていったのか、よく分からないですね

HAHAHAHAHA


[jekyll] GitHubのreleasesのリストからデータを引っ張って、GitHub Pagesのリリース一覧ページを作る

GitHubプロジェクトのreleasesからデータを引っ張って表示するGitHub Pages。
いろいろ手順・説明を省いて、要所だけ。

  • releases.html
    • テンプレートでレイアウトを指定。
---
layout: releases
---

do nothing to display.
  • _layouts/releases.html
    • 下を表示したい箇所に突っ込む。
{% for item in site.github.releases %}
  <h1>version: {{ item.name | default: item.tag_name }}</h1>
  {{ item.body | markdownify }}
  <div class="releases-download-link">
    <a href="{{ item.assets[0].browser_download_url }}">download {{ item.tag_name }}</a>
    published at: {{ item.published_at | date: "%Y-%m-%d %H:%M" }}
  </div>
{% endfor %}
  • site.github.releases でreleasesに入れたデータの一覧を取る。
  • データが空になりそうな箇所は、defaultフィルターで回避する。
  • markdownで書き込んでいる場合はHTML化の際、markdownifyフィルターを通しておく。
  • デザインは好きに変える。

お・わ・り。

electron-packagerのignoreオプションが余計なファイルを巻き込みすぎるのを何とかした

Electronのアプリを作成するelectron-packagerコマンドでは、
ignoreオプションでアプリに含めたくないファイルを除外できる。


最初のバージョン

最初はこのような感じでelectron-packagerコマンドを実行していた。

electron-packager . MYukkuriVoice --platform=darwin --arch=x64 --electronVersion=1.7.9 --icon=icns/myukkurivoice.icns \
  --overwrite --asar.unpackDir=vendor \
  --ignore="(\.gitignore|\.gitmodules|bin|docs|icns|test|README.md|vendor/aqk2k_mac|vendor/aqtk1-mac|vendor/aqtk2-mac|vendor/aqtk10-mac)"



でも、これは間違いだった。
ignoreオプションは、Regexpマッチでファイルを拾う。
このコマンド例では、ignoreオプションでbinとかtestとかdocsとかを指定していたので、
ファイルパスの一部にbinやtestやdocsが入っていたら、巻き込まれて作成するアプリから除外されてしまうのだ。
(そして、アプリが動かなくなった…)


どのように直せば良いか

electron-packagerのignoreオプションは複数指定できる。
そして、ignoreオプションのマッチングの仕組みは↓のようになっていて、
file変数にはパッケージのルートからのパス文字列が入っていた。

// node_modules/electron-packager/ignore.js
for (var i = 0; i < ignore.length; i++) {
  if (file.match(ignore[i])) {
    return false
  }
}



よって、次のようにignoreオプションを指定すれば、
余計なファイルを巻き込んで除外せずに済むようになる。

electron-packager . MYukkuriVoice --platform=darwin --arch=x64 --electronVersion=1.7.9 --icon=icns/myukkurivoice.icns \
  --overwrite --asar.unpackDir=vendor \
  --ignore="^/\.gitignore" \
  --ignore="^/\.gitmodules" \
  --ignore="^/bin" \
  --ignore="^/docs" \
  --ignore="^/icns" \
  --ignore="^/test" \
  --ignore="^/README.md" \
  --ignore="^/vendor/aqk2k_mac" \
  --ignore="^/vendor/aqtk1-mac" \
  --ignore="^/vendor/aqtk2-mac" \
  --ignore="^/vendor/aqtk10-mac"



でも、ちょっと待って

よく考えたら、使用しているnode_modulesの中に
テストコードとか、サンプルコードとか、ビルド用のファイルが含まれてる。
それらのファイルはアプリの実行に不要なのだから、除外すべきなのでは?
ドキュメントもasarに固めたら見えないし、要らないよね。


しかし、ignoreオプションで*の指定とかできないらしい(?)。


最終形はこうなりました(^_^;)

electron-packager . MYukkuriVoice --platform=darwin --arch=x64 --electronVersion=1.7.9 --icon=icns/myukkurivoice.icns \
  --overwrite --asar.unpackDir=vendor \
  --ignore="^/MYukkuriVoice-darwin-x64" \
  --ignore="^/README.md" \
  --ignore="^/.git" \
  --ignore="^/.gitignore" \
  --ignore="^/.gitmodules" \
  --ignore="^/bin" \
  --ignore="^/docs" \
  --ignore="^/icns" \
  --ignore="^/test" \
  --ignore="^/vendor/.gitignore" \
  --ignore="^/vendor/aqk2k_mac" \
  --ignore="^/vendor/aqtk1-mac" \
  --ignore="^/vendor/aqtk10-mac" \
  --ignore="^/vendor/aqtk2-mac" \
  --ignore=".DS_Store" \
  --ignore=".babelrc" \
  --ignore=".editorconfig" \
  --ignore=".eslintrc" \
  --ignore=".eslintrc.json" \
  --ignore=".jshintrc" \
  --ignore=".npmignore" \
  --ignore=".npmignore" \
  --ignore=".stylelintrc.json" \
  --ignore=".travis.yml" \
  --ignore="^/node_modules/about-window/LICENSE.txt" \
  --ignore="^/node_modules/about-window/README.md" \
  --ignore="^/node_modules/angular-input-highlight/README.md" \
  --ignore="^/node_modules/angular-input-highlight/test" \
  --ignore="^/node_modules/angular/LICENSE.md" \
  --ignore="^/node_modules/angular/README.md" \
  --ignore="^/node_modules/async/CHANGELOG.md" \
  --ignore="^/node_modules/async/LICENSE" \
  --ignore="^/node_modules/async/README.md" \
  --ignore="^/node_modules/audio-buffer-stream/README.md" \
  --ignore="^/node_modules/audio-buffer-stream/test" \
  --ignore="^/node_modules/balanced-match/LICENSE.md" \
  --ignore="^/node_modules/balanced-match/README.md" \
  --ignore="^/node_modules/bindings/README.md" \
  --ignore="^/node_modules/brace-expansion/README.md" \
  --ignore="^/node_modules/concat-map/LICENSE" \
  --ignore="^/node_modules/concat-map/README.markdown" \
  --ignore="^/node_modules/concat-map/example" \
  --ignore="^/node_modules/concat-map/test" \
  --ignore="^/node_modules/conf/license" \
  --ignore="^/node_modules/conf/readme.md" \
  --ignore="^/node_modules/core-util-is/LICENSE" \
  --ignore="^/node_modules/core-util-is/README.md" \
  --ignore="^/node_modules/core-util-is/test.js" \
  --ignore="^/node_modules/cryptico.js/README.md" \
  --ignore="^/node_modules/cryptico.js/sample" \
  --ignore="^/node_modules/debug/CHANGELOG.md" \
  --ignore="^/node_modules/debug/LICENSE" \
  --ignore="^/node_modules/debug/Makefile" \
  --ignore="^/node_modules/debug/README.md" \
  --ignore="^/node_modules/dot-prop/license" \
  --ignore="^/node_modules/dot-prop/readme.md" \
  --ignore="^/node_modules/electron-config/license" \
  --ignore="^/node_modules/electron-config/readme.md" \
  --ignore="^/node_modules/electron-is-accelerator/LICENSE" \
  --ignore="^/node_modules/electron-is-accelerator/README.md" \
  --ignore="^/node_modules/electron-is-accelerator/test.js" \
  --ignore="^/node_modules/electron-json-storage/CHANGELOG.md" \
  --ignore="^/node_modules/electron-json-storage/README.md" \
  --ignore="^/node_modules/electron-json-storage/doc" \
  --ignore="^/node_modules/electron-json-storage/tests" \
  --ignore="^/node_modules/electron-localshortcut/license" \
  --ignore="^/node_modules/electron-localshortcut/readme.md" \
  --ignore="^/node_modules/electron-log/LICENSE" \
  --ignore="^/node_modules/electron-log/README.md" \
  --ignore="^/node_modules/env-paths/license" \
  --ignore="^/node_modules/env-paths/readme.md" \
  --ignore="^/node_modules/exists-file/CHANGELOG.md" \
  --ignore="^/node_modules/exists-file/LICENSE.md" \
  --ignore="^/node_modules/ffi/CHANGELOG.md" \
  --ignore="^/node_modules/ffi/LICENSE" \
  --ignore="^/node_modules/ffi/README.md" \
  --ignore="^/node_modules/ffi/deps" \
  --ignore="^/node_modules/ffi/example" \
  --ignore="^/node_modules/ffi/src" \
  --ignore="^/node_modules/ffi/test" \
  --ignore="^/node_modules/find-up/license" \
  --ignore="^/node_modules/find-up/readme.md" \
  --ignore="^/node_modules/fs.realpath/LICENSE" \
  --ignore="^/node_modules/fs.realpath/README.md" \
  --ignore="^/node_modules/glob/LICENSE" \
  --ignore="^/node_modules/glob/README.md" \
  --ignore="^/node_modules/glob/changelog.md" \
  --ignore="^/node_modules/inflight/LICENSE" \
  --ignore="^/node_modules/inflight/README.md" \
  --ignore="^/node_modules/inherits/LICENSE" \
  --ignore="^/node_modules/inherits/README.md" \
  --ignore="^/node_modules/intro.js/CODE_OF_CONDUCT.md" \
  --ignore="^/node_modules/intro.js/CONTRIBUTING.md" \
  --ignore="^/node_modules/intro.js/Makefile" \
  --ignore="^/node_modules/intro.js/README.md" \
  --ignore="^/node_modules/intro.js/changelog.md" \
  --ignore="^/node_modules/intro.js/docs" \
  --ignore="^/node_modules/intro.js/example" \
  --ignore="^/node_modules/intro.js/license.md" \
  --ignore="^/node_modules/is-obj/license" \
  --ignore="^/node_modules/is-obj/readme.md" \
  --ignore="^/node_modules/isarray/README.md" \
  --ignore="^/node_modules/lodash/LICENSE" \
  --ignore="^/node_modules/lodash/README.md" \
  --ignore="^/node_modules/minimatch/LICENSE" \
  --ignore="^/node_modules/minimatch/README.md" \
  --ignore="^/node_modules/minimist/LICENSE" \
  --ignore="^/node_modules/minimist/example" \
  --ignore="^/node_modules/minimist/readme.markdown" \
  --ignore="^/node_modules/minimist/test" \
  --ignore="^/node_modules/mkdirp/LICENSE" \
  --ignore="^/node_modules/mkdirp/bin/usage.txt" \
  --ignore="^/node_modules/mkdirp/examples" \
  --ignore="^/node_modules/mkdirp/readme.markdown" \
  --ignore="^/node_modules/mkdirp/test" \
  --ignore="^/node_modules/ms/LICENSE.md" \
  --ignore="^/node_modules/ms/README.md" \
  --ignore="^/node_modules/nan/CHANGELOG.md" \
  --ignore="^/node_modules/nan/LICENSE.md" \
  --ignore="^/node_modules/nan/README.md" \
  --ignore="^/node_modules/nan/doc" \
  --ignore="^/node_modules/nan/nan.h" \
  --ignore="^/node_modules/nan/nan_callbacks.h" \
  --ignore="^/node_modules/nan/nan_callbacks_12_inl.h" \
  --ignore="^/node_modules/nan/nan_callbacks_pre_12_inl.h" \
  --ignore="^/node_modules/nan/nan_converters.h" \
  --ignore="^/node_modules/nan/nan_converters_43_inl.h" \
  --ignore="^/node_modules/nan/nan_converters_pre_43_inl.h" \
  --ignore="^/node_modules/nan/nan_implementation_12_inl.h" \
  --ignore="^/node_modules/nan/nan_implementation_pre_12_inl.h" \
  --ignore="^/node_modules/nan/nan_maybe_43_inl.h" \
  --ignore="^/node_modules/nan/nan_maybe_pre_43_inl.h" \
  --ignore="^/node_modules/nan/nan_new.h" \
  --ignore="^/node_modules/nan/nan_object_wrap.h" \
  --ignore="^/node_modules/nan/nan_persistent_12_inl.h" \
  --ignore="^/node_modules/nan/nan_persistent_pre_12_inl.h" \
  --ignore="^/node_modules/nan/nan_string_bytes.h" \
  --ignore="^/node_modules/nan/nan_typedarray_contents.h" \
  --ignore="^/node_modules/nan/nan_weak.h" \
  --ignore="^/node_modules/nan/tools/README.md" \
  --ignore="^/node_modules/once/LICENSE" \
  --ignore="^/node_modules/once/README.md" \
  --ignore="^/node_modules/os-tmpdir/license" \
  --ignore="^/node_modules/os-tmpdir/readme.md" \
  --ignore="^/node_modules/path-exists/license" \
  --ignore="^/node_modules/path-exists/readme.md" \
  --ignore="^/node_modules/path-is-absolute/license" \
  --ignore="^/node_modules/path-is-absolute/readme.md" \
  --ignore="^/node_modules/photon/CNAME" \
  --ignore="^/node_modules/photon/CONTRIBUTING.md" \
  --ignore="^/node_modules/photon/LICENSE" \
  --ignore="^/node_modules/photon/README.md" \
  --ignore="^/node_modules/photon/docs" \
  --ignore="^/node_modules/photon/sass" \
  --ignore="^/node_modules/pinkie-promise/license" \
  --ignore="^/node_modules/pinkie-promise/readme.md" \
  --ignore="^/node_modules/pinkie/license" \
  --ignore="^/node_modules/pinkie/readme.md" \
  --ignore="^/node_modules/pkg-up/license" \
  --ignore="^/node_modules/pkg-up/readme.md" \
  --ignore="^/node_modules/readable-stream/LICENSE" \
  --ignore="^/node_modules/readable-stream/README.md" \
  --ignore="^/node_modules/ref-struct/History.md" \
  --ignore="^/node_modules/ref-struct/README.md" \
  --ignore="^/node_modules/ref/CHANGELOG.md" \
  --ignore="^/node_modules/ref/README.md" \
  --ignore="^/node_modules/ref/build/Makefile" \
  --ignore="^/node_modules/ref/build/Release/.deps/Release/obj.target/binding/src" \
  --ignore="^/node_modules/ref/build/Release/obj.target/binding/src" \
  --ignore="^/node_modules/ref/build/binding.Makefile" \
  --ignore="^/node_modules/ref/build/binding.target.mk" \
  --ignore="^/node_modules/ref/docs" \
  --ignore="^/node_modules/ref/src" \
  --ignore="^/node_modules/ref/test" \
  --ignore="^/node_modules/rimraf/LICENSE" \
  --ignore="^/node_modules/rimraf/README.md" \
  --ignore="^/node_modules/stream-parser/History.md" \
  --ignore="^/node_modules/stream-parser/LICENSE" \
  --ignore="^/node_modules/stream-parser/README.md" \
  --ignore="^/node_modules/stream-parser/node_modules/debug/.npmignore" \
  --ignore="^/node_modules/stream-parser/node_modules/debug/History.md" \
  --ignore="^/node_modules/stream-parser/node_modules/debug/Makefile" \
  --ignore="^/node_modules/stream-parser/node_modules/debug/Readme.md" \
  --ignore="^/node_modules/stream-parser/node_modules/ms/license.md" \
  --ignore="^/node_modules/stream-parser/node_modules/ms/README.md" \
  --ignore="^/node_modules/stream-parser/test" \
  --ignore="^/node_modules/string_decoder/LICENSE" \
  --ignore="^/node_modules/string_decoder/README.md" \
  --ignore="^/node_modules/temp/LICENSE" \
  --ignore="^/node_modules/temp/README.md" \
  --ignore="^/node_modules/temp/examples" \
  --ignore="^/node_modules/temp/node_modules/rimraf/AUTHORS" \
  --ignore="^/node_modules/temp/node_modules/rimraf/LICENSE" \
  --ignore="^/node_modules/temp/node_modules/rimraf/README.md" \
  --ignore="^/node_modules/temp/node_modules/rimraf/test" \
  --ignore="^/node_modules/temp/test" \
  --ignore="^/node_modules/tunajs/CONTRIBUTE.md" \
  --ignore="^/node_modules/tunajs/README.md" \
  --ignore="^/node_modules/tunajs/tests" \
  --ignore="^/node_modules/wav/History.md" \
  --ignore="^/node_modules/wav/README.md" \
  --ignore="^/node_modules/wav/examples" \
  --ignore="^/node_modules/wav/test" \
  --ignore="^/node_modules/wave-recorder/README.md" \
  --ignore="^/node_modules/wave-recorder/example.js" \
  --ignore="^/node_modules/wrappy/LICENSE" \
  --ignore="^/node_modules/wrappy/README.md" \
  --ignore="^/node_modules/xtend/LICENCE" \
  --ignore="^/node_modules/xtend/Makefile" \
  --ignore="^/node_modules/xtend/README.md" \
  --ignore="^/node_modules/xtend/test.js"



ち・か・ら・お・し!


これ、絶対、保守性悪いわ…
もうやっちゃったから、このまま残すけど…
たいしたアプリサイズの削減にもならないし、皆は、ここまではやらない方が良いぞ(^_^;)

Electronアプリから、別のアプリへファイルのドラッグアンドドロップによる受け渡しを実現する

経緯

Electronのアプリで音声ファイルを生成したら、その音声ファイルを直接、動画編集ソフトにドロップしたかった。
これが出来ると、音声録音→動画編集ソフトに突っ込み、の作業をポイポイできるようになる。

ファーストトライ

駄目だったパターン。これ(↓)を参考に、Electronのアプリに組み込んでみた。
Drag out files like Gmail
https://www.thecssninja.com/javascript/gmail-dragout


この方法では、ファインダーにドラッグアンドドロップする分にはファイルを渡せるのだけれど、
動画編集ソフトにリンクをドロップしようとしても反応してくれなかった。

<html>
<head>
</head>
<body>
<a id="dragout" draggable="true"
    data-downloadurl="application/octet-stream:photo-image.png:file:///Users/taku-o/Desktop/photo-image.png">
    これをドラッグアンドドロップ</a>

<script>
var file = document.getElementById("dragout");

var fileDetails;

if(typeof file.dataset === "undefined") {
  fileDetails = file.getAttribute("data-downloadurl");
} else {
  fileDetails = file.dataset.downloadurl;
}

file.addEventListener("dragstart",function(evt){
  evt.dataTransfer.setData("DownloadURL", fileDetails);
},false);
</script>

</body>
</html>



これで出来たぞ!

Electronのrendererプロセスでdragstartイベントリスナーを設定。
dragstartイベントを補足したら、mainプロセスに通知。
mainプロセス側で処理すれば、動画編集ソフトへの直接ドラッグアンドドロップを実現できた。

// rendererプロセス
.directive('wavDraggable', function($parse) {
  return function(scope, element, attr) {

    scope.$watch('last_wav_file', function(value) {
      var message = value;
      var wav_file_path = message.wav_file_path;

      var el = element[0];
      el.draggable = true;
      el.addEventListener(
        'dragstart',
        function(e) {
          e.preventDefault();
          ipcRenderer.send('ondragstartwav', wav_file_path)
          return false;
        },
        false
      );
    };
  }
})
// mainプロセス
ipcMain.on('ondragstartwav', function (event, filePath) {
  var imgPath = path.join(__dirname, '/img/ic_music_video_black_24dp_1x.png');
  event.sender.startDrag({
    file: filePath,
    icon: imgPath
  })
});

https://raw.githubusercontent.com/taku-o/myukkurivoice/master/docs/images/readme-dnd.gif


まとめ

これは、Electron特有の事情、ということで良いのかな?
nw.jsのissue経由で対応方法を発見した。
ありがとうnw.jsコミュニティ!


https://github.com/nwjs/nw.js/issues/5256
https://github.com/nwjs/nw.js/issues/2200
https://github.com/electron/electron/pull/6333