↑ ChatGPT API (OpenAI API) でチャットサービスを開発しました。マークダウンのコピペをしやすいように実装しています。まだ自分自身 OpenAI API を使用して日が浅いため ChatGPT PLUS より安いとは断言できませんが、おそらく安く済みます。機能などの紹介は note でしているため Qiita では技術的なお話をメインでします。
ソースコードはこちらです。
スクリーンショット
ChatGPT Clone ですね。チャットシステムとか開発したことなかったので、勉強がてら Svelte Kit を使用して開発しました。
ChatGPT との違いは「マークダウン有効無効ボタン」、「コードのテーマ変更」、「使用料金の表示」ですね。
技術スタック
大まかに以下を使用しています。
- Svelte Kit
- XState
- Vite
- Ionicframework
- Capacitor.js
Svelte Kit と Ionicframework
デザインは Ionicframework に全任せしています。以前、 Tailwindcss などを使用していましたが、個人開発においては Ionicframework に軍配が上がりますね。 Ionicframework は本当に使いやすく、まさに Web Component の王様ですね。ただし、 Svelte で Ionicframework を使用するときにやや使いづらい点があります。それは slot
被りです。
Slot 問題
Svelte での slot
は、コンポーネントの slot tag の name として使用されます。
<slot name="header" />
// ↑ これにコンポーネントを挿入したい時は以下のように書く
<span slot="header" />
この slot
が Ionicframework とコンフリクトしています。 Ionicframework での slot は、コンポーネントの位置を指定するために使用します。
<ion-item>
<ion-icon name="star" slot="start"></ion-icon>
<ion-label>Default Item Lines</ion-label>
<ion-icon name="information-circle" slot="end"></ion-icon>
</ion-item>
そのため Svelte を使用しているときは、 ルートで slot を書くことができません。以下のような分離されたコンポーネントは作成できません。
<ion-icon name="star" slot="start"></ion-icon>
Element with a slot='...' attribute must be a child of a component or a descendant of a custom elementsvelte(invalid-slotted-content)
残念ですが、この場合は、 onMount などを使用し、 setAttribute で slot を更新するなどの方法が必要です。
on:click などのイベント問題
svelte でのイベントは on:click
などで実行します。
<span on:click={() => console.log("click")} />
しかし、Ionicframework でのイベントは onIonClick
という名前になっており、 on:IonClick
ではトリガーを発生させることができませんでした。onIonClick としても発火しません。 on の次の名前は小文字ではないと発火しないのかもしれません。対策として、on:ionClick
となるような形定義を作ります。以下のように書きます。
すると、
<ion-button on:ionClick={(e) => console.log(e.detail.value)} />
で無事に発火できるようになります。
XState
これはチャットする箇所のロジックを全てステートマシンとして実装しています。チャットのような、ロード、待機、などのステータスが時間で変化するときに最適です。
Capacitor.js
これは Web アプリをネイティブアプリに変換してくれるツールです。今回は、データ保存用として使用しています。今のところネイティブアプリにする予定はないですが、使用していることですぐにネイティブアプリ化できます。素晴らしい。
あとがき
自分自身が、 ChatGPT PLUS を辞めて、使用するツールとして開発しました。安く済むといいなぁ。