walkingmask’s development log

IT系の情報などを適当に書いていきます

MENU

ブックマークをPATHのように扱えるChrome拡張を作った

Slash Bookmarks

ソースコード

前回の作った平成ドローに比べると地味だけど、これもずっと作りたかったものを形にできたので最高。

開発中のログを加筆修正・まとめつつ載せておく。

デモ

DEMO GIF

アイディア

2018/03/17 にはアイディアがメモされている。割と新しい。

コンセプト

ロームオムニボックスで、ブックマークをパス(フォルダ階層)指定で開けるようにする拡張機能。Autocomplete 付き。

ネーミング

ブックマークをパスのように扱う、パスと言えば "/" なので (Windows ユーザに怒られそうだけど)。

開発スケジュール

10/13 名前考案、参考文献集め、初期コーディング
10/14 ゆっくりコーディング
10/15 夜作業
10/16 夜作業
10/17 夜作業

体調を崩してたので、休みを兼ねてゆっくりコーディング。

時間は、毎日6時間前後ぐらいか。サクッと作るつもりだったけど思いの外 Chrome API に詰まった。

参考文献

作り方

manifest.jsonの書き方

サンプルコード

公式ドキュメント

特にfalconは実際にインストールしたり、とても参考にした。感謝。

開発方針

chrome.bookmarks.getTree 等でブックマークを木構造で取れることがすぐわかったので、これを利用することに。

ただ、毎回ルートノードから探索するのではアホすぎるので、入力やノードを保存していくことにした。

開発

最初は作り方のページを参考に、とりあえず拡張用のディレクトリを作成して、最小構成でブラウザにインストールして眺めてた。

manifest の各項目がわからなくて調べたりしつつ、サンプルコードを探す。

icon 作ったり。全部 mac のプレビューで作った。あとあと公開するときのパネル画像とかも全部。mac すごい。

Browser Action とか Page Action とか Contant Page とかわからないので、ググって眺めたりしてた。

default suggestion を出すことに成功。この辺から詰まる。

とりあえず動作フローを考える。

  1. オムニボックスに "/" + (space or tab) でフック
  2. "Bookmarks Bar", "Other Bookmarks", "Mobile Bookmarks" を表示
  3. ユーザの打ち込みをフックしてサジェスト表示
    • "B" と打ったら "Bookmarks Bar" だけになる
  4. "↑"" or "↓" で選択、"/" でフォルダを次の階層へ移しサジェスト表示
  5. Enter でその URL へ飛ぶ

これに近いサンプルコードを探し、falcon を発見。

各 omnibox の Event Listener の動作がイマイチわからなかったので確認する。

  • onInputStarted: 最初の1文字が入力されると発火。拡張スタート時点じゃない
  • onInputChanged: omnibox の入力が変更されるたびに発火
    • deleteも
    • カーソル移動は発火しない
    • suggenstions の移動も発火しない
    • suggestions 選択後、新たな入力をすると発火
  • onInputEntered: エンターすると発火。完全にそれだけ
  • onInputCancelled: おそらく onInputStarted 発火後、以下の動作で発火
    • 拡張のsuggestion以外のサジェスチョンを選択する
    • escで抜ける
    • deleteで全文字削除して拡張を抜ける
    • omniboxのフォーカスを外す

Started と Cancelled は活躍しないことが決定。

falcon を参考にガシガシ書くが、やはり動作フローがもやっとして、なぜか強化学習の知識を使って定義する。UML とか真面目に勉強すればよかった。

ユーザの行動空間

  • 導線
    • omniboxに/+space or tabで/Bに入る
    • chromeの履歴サジェストから/Bに入る
    • chromeサジェスチョンから /B サジェスチョンへバック
  • 入力
    • 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日取られた気がするけど。

あとはオプション用のページ作ったりして、ルートディレクトリを変更できるようにして、

デベロッパー登録してリリース!

f:id:walkingmask:20181018115729g:plain

まとめ

拡張機能は今後も作る予定があったので、練習を兼ねて作った。

作りたかったものを作って、動いた瞬間は本当に最高!

ただ、この機能自体は需要が自分くらいにしかないことはわかってる。