ブックマークをPATHのように扱えるChrome拡張を作った
前回の作った平成ドローに比べると地味だけど、これもずっと作りたかったものを形にできたので最高。
開発中のログを加筆修正・まとめつつ載せておく。
デモ
アイディア
2018/03/17 にはアイディアがメモされている。割と新しい。
コンセプト
クロームのオムニボックスで、ブックマークをパス(フォルダ階層)指定で開けるようにする拡張機能。Autocomplete 付き。
ネーミング
ブックマークをパスのように扱う、パスと言えば "/" なので (Windows ユーザに怒られそうだけど)。
開発スケジュール
10/13 名前考案、参考文献集め、初期コーディング 10/14 ゆっくりコーディング 10/15 夜作業 10/16 夜作業 10/17 夜作業
体調を崩してたので、休みを兼ねてゆっくりコーディング。
時間は、毎日6時間前後ぐらいか。サクッと作るつもりだったけど思いの外 Chrome API に詰まった。
参考文献
作り方
manifest.jsonの書き方
サンプルコード
公式ドキュメント
- https://developer.chrome.com/extensions/bookmarks
- https://developer.chrome.com/extensions/omnibox
- https://developer.chrome.com/apps/storage
- https://developer.chrome.com/extensions/options
特にfalconは実際にインストールしたり、とても参考にした。感謝。
開発方針
chrome.bookmarks.getTree 等でブックマークを木構造で取れることがすぐわかったので、これを利用することに。
ただ、毎回ルートノードから探索するのではアホすぎるので、入力やノードを保存していくことにした。
開発
最初は作り方のページを参考に、とりあえず拡張用のディレクトリを作成して、最小構成でブラウザにインストールして眺めてた。
manifest の各項目がわからなくて調べたりしつつ、サンプルコードを探す。
icon 作ったり。全部 mac のプレビューで作った。あとあと公開するときのパネル画像とかも全部。mac すごい。
Browser Action とか Page Action とか Contant Page とかわからないので、ググって眺めたりしてた。
default suggestion を出すことに成功。この辺から詰まる。
とりあえず動作フローを考える。
- オムニボックスに "/" + (space or tab) でフック
- "Bookmarks Bar", "Other Bookmarks", "Mobile Bookmarks" を表示
- ユーザの打ち込みをフックしてサジェスト表示
- "B" と打ったら "Bookmarks Bar" だけになる
- "↑"" or "↓" で選択、"/" でフォルダを次の階層へ移しサジェスト表示
- Enter でその URL へ飛ぶ
これに近いサンプルコードを探し、falcon を発見。
各 omnibox の Event Listener の動作がイマイチわからなかったので確認する。
- onInputStarted: 最初の1文字が入力されると発火。拡張スタート時点じゃない
- onInputChanged: omnibox の入力が変更されるたびに発火
- deleteも
- カーソル移動は発火しない
- suggenstions の移動も発火しない
- suggestions 選択後、新たな入力をすると発火
- onInputEntered: エンターすると発火。完全にそれだけ
- onInputCancelled: おそらく onInputStarted 発火後、以下の動作で発火
- 拡張のsuggestion以外のサジェスチョンを選択する
- escで抜ける
- deleteで全文字削除して拡張を抜ける
- omniboxのフォーカスを外す
Started と Cancelled は活躍しないことが決定。
falcon を参考にガシガシ書くが、やはり動作フローがもやっとして、なぜか強化学習の知識を使って定義する。UML とか真面目に勉強すればよかった。
ユーザの行動空間
- 導線
- 入力
- 1文字入力
- 複数文字ペースト
- 削除
- 1文字消去
- カーソル選択による複数文字削除
- 選択
- /B suggestionの選択
- chromeサジェスチョンの選択
- 決定
- /B suggestionでEnter
- キャンセル
- esc
- フォーカスを外す
- /B から delete 押して /B から抜ける
状態空間
- 未入力状態
- /が1つもない入力状態 (/B abc)
- /が末尾にある入力状態 (/B abc/)
- /を含む、/以外が末尾の入力状態 ( /B abc/de)
- suggestion選択状態
記号化
状態空間 S0: not /B S1: /B S2: /B abc S3: /B abc/ S4: /B abc/de Sp: /B suggested PATH Su: /B suggested URL #Sc: /B /path/to/URL # 考えにくい 行動空間 Ae1: 導線、/ + space or tab Ae2: 導線、chrome履歴から Ae3: 導線、chromeサジェスチョンからバック Ai: Ai1: 1文字入力 Ai2: 文字ペースト Ad: Ad1: 1文字削除 Ad2: 選択文字削除 As1: /Bサジェスチョンの選択 As2: chromeサジェスチョンの選択 Adec: /Bサジェスチョンの決定 Acan: Aesc: Escキーでキャンセル Aout: フォーカス外す Adel: /Bでdelを押して抜ける
これを使って、各状態から取りうる行動と、その行動を取ったときの状態遷移を列挙した。
そこから得られた結論は、ペースト、選択削除とサジェスチョンバックがややこしすぎて、「いかなる行動を受け取っても、状態を常に正しく保ち、状態に応じたサジェスチョンを表示」という身も蓋もない感じに。
ただこれで状態を保つクラスさえ作ればあとは楽ということがわかった。
あとは上記の状態行動表はあとあと追加削除したり。
ここまでくると割とサクッとメイン部分は実装できた。細かいチューニングに1日取られた気がするけど。
あとはオプション用のページ作ったりして、ルートディレクトリを変更できるようにして、
デベロッパー登録してリリース!
まとめ
拡張機能は今後も作る予定があったので、練習を兼ねて作った。
作りたかったものを作って、動いた瞬間は本当に最高!
ただ、この機能自体は需要が自分くらいにしかないことはわかってる。