⚙️

Raycast で社内ドキュメント検索ツールを作ってみた

2022/02/16に公開

はじめに

最近話題の Raycast ですが、React で簡単に extension を簡単に作れることも魅力の一つです。
社内の生産性を向上させる Developer eXperience 宛に「Raycast を使って社内ドキュメントを簡単に探せるようにできないか?」という社内の要望をもらったのでこれに答える用とするとどうなるのかを探っていき、これを通して Raycast の extension を作るときの Tips を紹介していきます。

https://www.raycast.com/

できたもの

検索窓に知りたい項目を入れて private repository のドキュメントを探せる。

ブラウザに飛ぶこともできる。

内容のプレビューもできる。

実装

コードは下のレポジトリに公開しています。
https://github.com/wantedly/raycast-dev-docs

Token 取得方法

今回は社内ドキュメントの検索なので、アクセストークンを取得する必要があります。
package.json に下のように書いておくと、extension に固有な設定を保存することができるので今回のようなケースに向いています。

package.json
  "preferences": [
    {
      "name": "token",
      "type": "password",
      "required": true,
      "title": "Personal Access Token",
      "description": "Create a token with repository access"
    }
  ],

このように記載しておくと、初回起動時に下のような表示が出てユーザーに入力を強制させることができます。

公式ドキュメントはこちらです。
https://developers.raycast.com/api-reference/preferences

ファイル検索

下のようなコードで検索するようにしました。ここでは wantedly/dev という private な repository 内検索にしているので、これを preference に移行したり、空にしたりするともっと汎用性のあるものにできるでしょう。コード検索においては GraphQL の API が使えなかったので少しリストで表示するのに情報が乏しいなという印象があります。他の extension を作るときは GraphQL API で必要な情報をとってくると良いかもしれません。debug していると API rate を exceed してしまうことがあるのでもうちょっと工夫がいるかも知れません。

const useFiles = (q: string | undefined): { files: File[]; isLoading: boolean } => {
  if (q === undefined) {
    return { files: [], isLoading: true };
  }
  const [state, setState] = useState<File[] | undefined>(undefined);
  useEffect(() => {
    (async () => {
      setState(undefined);
      const token = getPreferenceValues().token;
      const octokit = new Octokit({ auth: token });
      const res = await octokit.rest.search.code({ q: `repo:wantedly/dev language:Markdown ${q}` });
      setState(res.data.items);
    })();
  }, [q]);
  if (state !== undefined) {
    return { files: state, isLoading: false };
  }
  return { files: [], isLoading: true };
};

List 表示

リストの一行は下のコードで展開されています。 <Preview /> は GitHub の file の情報から <Detail /> を呼び出すだけのシンプルなお手製コンポーネントです。

      <List.Item
        icon={Icon.Finder}
        title={f.path}
        key={f.git_url}
        actions={
          <ActionPanel>
            {/* Primary のアクション => `↵` で呼び出し */}
            <Action.Push title="Show Details" target={<Preview file={f} />} />
            {/* Secondary のアクション => `⌘ ↵` で呼び出し */}
            <Action.OpenInBrowser url={f.html_url} />
          </ActionPanel>
        }
      />

<List /> の API については下のリンクを参考にすると良いでしょう。
https://developers.raycast.com/api-reference/user-interface/list

Publish 方法

とりあえず勢いで作ってみたものの、社員に展開する方法としては、各自で build して貰う方法を除けばどうやら公式レポジトリに登録してもらうしかなさそうだということがわかりました。すると社内特有の事情をハードコードをしたものはレビューとして通してもらえないと思うので、やるとしてももうちょっと一般化したあとになりそうです。

Tips

最後に Raycast で extension を作る Tips を紹介します。

ドキュメントを読み込む

公式のドキュメントに必要なことはほぼ全て書いてあります。困ったらなにかドキュメントを呼んでみると良いでしょう。
https://developers.raycast.com/

既存の extension を参考にする

ユーザーが作った extension は全て下の repository で実装が公開されています。なにかやりたいことがあったら他の extension がどのようにしているのかを参考にすると良いでしょう。
https://github.com/raycast/extensions

自作 extension には非公開の API もあることを知っておく


例えば上の画像のような List の横に何かを置くような UI は現時点では自作 extension では実装が不可能である様子です。下のような issue で Feature Request として言及されています。

https://github.com/raycast/extensions/issues/83

このようなことがあるので実現したい UI/UX がある場合に、それが他の自作 extension で達成されているかどうかをリサーチすると時間を溶かさなくて良くなります。

最後に

Raycast に extension を作るのが React で書けて便利でした。今回は社内特有の事情が含まれるものを作ってしまったので簡単に install できるという魅力を捨ててしまっていますが、少し工夫すれば汎用的なものにもできると思うので気が向いたらやってみようと思っています。(代わりやりたい人が社内外問わずいたら教えて下さい。)

Discussion