3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React初心者がRaycastの拡張機能を作ってみた

Last updated at Posted at 2024-05-06

MacのランチャーアプリRaycast

Raycastとは、macOS向けの多機能かつシンプルなランチャーアプリです。
https://www.raycast.com/

ショートカットや数文字のキーボード入力(Alias機能)により、アプリの起動やさまざまなコマンドを実行できます。Alfredをより拡張性を高くしたようなイメージです。
主な機能として以下のようなものがあり、さらにExtensionによって機能を拡張できます。

  • アプリケーション起動
  • ファイル検索
  • クリップボード履歴管理
  • 計算機機能
  • ブックマーク検索
  • クイックリンク作成(クエリを入れられるので、Qiita検索や乗換案内検索もつくれる)
  • ターミナルコマンド実行
  • スクリプト実行
  • ウィンドウマネジメント
  • スニペット

またほとんどの機能を無料で使えます。
効率化アプリをいろいろ入れた結果、ショートカットが増えすぎて困っている方は、置き換え検討しても良いかもしれません。

今回作ったもの

Raycastの魅力の一つとして、多くの拡張機能が無料で公開されていること、さらには自分でも拡張機能を作成できることがあります。
今回作ったのは、クリップボードにある文字列を変換して、またクリップボードにコピーする、テキスト変換の機能拡張です。
変換内容はドロップダウンで選べるようになっており、空行や行頭の空白削除、変わったところではOutlookの宛先からメールアドレスのみ抽出する変換などを入れてみました。今後も必要に応じて変換内容を追加していく予定です。
これまでエディタの正規表現を使ってやっていたちょっとしたテキスト変換を、もっと手軽にできるようにしようと思って作成しました。
image.png

拡張機能の作り方

拡張機能はReactを使った開発になります。
Raycast公式でしっかりした環境を用意されていること、テンプレートやドキュメントがしっかりしていることから、割と初心者でもとっつきやすいつくりになっています。私はReactどころか、JavaScriptもTypeScriptもほとんど使ったことがない状態でしたが、Reactの公式チュートリアルと、Raycastの公式ドキュメントを眺めながら、なんとか自作Extensionを作成できました。

準備

Extentionの設定でDeveloperのCreateExtensionにチェックがついていることを確認し、
image.png
Raycast上で、Create Extensionと入力する(数文字入力すれば特定されます)
image.png

テンプレートの種類やコマンド名が聞かれるので、記入

項目名 説明 入力値
Organization Store公開時の組織名 None
Template 用意されているテンプレート種類 Form
Extension Name* 名前。Storeや設定時に表示 お好きに
Description 詳細情報。Storeや設定時に表示 -
Categories カテゴリー情報。Storeで使用 -
Location プロジェクトを作成するディレクトリ お好きな場所で
Command Name* 名前。Raycast検索時に表示される 探しやすい文字列で
Subtitle 名前の補足。Raycast検索時に表示される -
Description 詳細情報。設定時に表示される -

入力後にcmd + enterでコードが生成されます。

ターミナルを開き、生成されたディレクトリでnpm install && npm run devと入力すると、テンプレートの機能拡張が動くようになっています。
run devとしていることで、ソースコードを書き換えると、自動的にビルド・インストールが実行されます。

このあたりは公式のチュートリアル https://developers.raycast.com/basics/create-your-first-extension に記述があります。

カスタム拡張機能の作成

このテンプレートをもとに、開発していきます。
コードの中身はsrc/index.tsxに記載されています。

これを書き換えてカスタムしていきます。
書き換え後のコードを掲載します。

import { ActionPanel, Form, Icon, showToast, Toast, Action, Clipboard } from "@raycast/api";
import { useState, useEffect } from "react";

export default function Command() {
  //入力文字列と出力文字列を保持できる形で用意
  const [input, setInput] = useState<string>('');
  const changeInput =(newValue: string) => {
    setInput(newValue);
  };
  const [output, setOutput] = useState<string>('ここには結果が出力される');
  const changeOutput =(newValue: string) => {
    setOutput(newValue);
  };

  //初回のみ実行(クリップボードの内容をインプットフォームに読み込み)
  useEffect(() => {
    InputCopiedText(changeInput);
  }, []);

  return (
    <Form
      actions={
        <ActionPanel>
          <RegAction changeOutput={changeOutput} />
        </ActionPanel>
      }
    >
      <Form.TextArea id="orgText" title="Original Text" placeholder='変換したいテキストを入れてください' value={input} />
      <Form.Dropdown id="regNo" title="RegEx list" storeValue>
        <Form.Dropdown.Item value="0" title="Outlookからメアド抽出->カンマ連結" />
        <Form.Dropdown.Item value="1" title="空行削除1" />
        <Form.Dropdown.Item value="2" title="行頭空白削除" />
      </Form.Dropdown>
      <Form.Description title="結果出力" text={output} />

    </Form>


  );
}

function RegAction({changeOutput}) {
  async function handleSubmit(values: { orgText: string; regNo: number; }) {

    if (!values.orgText) {
      showToast({
        style: Toast.Style.Failure,
        title: "変換したいテキストを入れてください",
      });
      return;
    }

    const toast = await showToast({ style: Toast.Style.Animated, title: "変換中" });

    // ここに正規表現と置換のリストを作成する
    const regExs =[
      {
        reg:      /(.+?) <(.+?)>((; )|$)/g,
        replace:  "$2,"
      },
      {
        reg:      /^\n/gm,
        replace:  ""
      },
      {
        reg:      /^( |\t| )+/gm,
        replace:  ""
      }
    ];

    // ここで正規表現に変換
    const i = values.regNo;
    const afterText = values.orgText.replace(regExs[i].reg, regExs[i].replace);
    changeOutput(afterText);

    //結果をクリップボードにコピーする
    await Clipboard.copy(afterText);

    //正常終了したことをステータス表示
    toast.style = Toast.Style.Success;
    toast.title = "Success";
    toast.message = "Copied text to clipboard";

  }

  return <Action.SubmitForm icon={Icon.Upload} title="RegEx Text" onSubmit={handleSubmit} />;
}

//クリップボードの内容をインプットフォームに入れる関数
async function InputCopiedText(changeInput) {
  const text = await Clipboard.readText();
  changeInput(text);
  return;
}

動作画面はこちら
RegForm.gif

UIの部品はテンプレートで用意されているものをそのまま使い、そこに入出力するだけの簡単な内容です。
ただReactが初めてだったこともあり、更新タイミングのコントロールをどうするのか、propなどをどう渡せばよいかなどで結構悩みました。
書き方はかなり拙いですが、とりあえず動くものができたので満足です。

Reactを試す環境としても、CSSなどで悩まずに簡単に動作するものを作れるので、初心者の勉強としても良さそうです。

ほぼ同じものはスクリプトでもできそうな気もしますが、クリップボードの操作やメニューの操作などもRaycastが用意してくれているので、ちょっとした効率化アプリをつくる環境として重宝しそうです。

もっと精進して、さまざまなアプリの動作をほとんどraycastで行えるようになりたいなあ。

参考にしたページ

比較的シンプルなもの

DevelopersIO | 自作Raycast Extensionでお手軽生産性アップ

解説が丁寧

今後目指したい

各種公式ページ

最近は公式ページの解説がとても充実していて本当に助かります。
単に詳しいだけでなく、初心者向けの解説が用意されているのがありがたいですね。

最後に

先人のみなさんのおかげで、やってみようと思えました。
ありがとうございます。
初心者の自分でも動くものが作れたということで、誰かの後押しになれればよいかと思い、記事を公開しました。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?