Phantom Frame

https://github.com/balckowl/progate-lp

Next.js

React

AWS

PostgreSQL

Rust

ソフトウェアを美しく魅せるデスクトップアプリ

ネッシー

yasubee

potekichi

Somahc

cooper night

推しアイデア

スクショ撮影・画面録画を、Rustを使って高速に処理できる!!

作った背景

類似アプリは有料のため、オリジナルの機能を入れたい!!

推し技術

「ラスト」というテーマに合わせて、Rustを使ったこと!!

プロジェクト詳細

作品概要

Webサイトを美しく魅せるデスクトップアプリ。

主な機能

このアプリは「ポートレート機能」と「動画撮影機能」を提供します。

✨ ポートレート機能

  • プロダクトのスクリーンショットを、自由な背景上で装飾・編集できる。

✨ 動画撮影

  • プロダクトの操作動画を録画すると、マウスの動きに応じて画面が自動的に拡大・縮小される。

技術スタック

サービス

image

✨ サイト側

Next.jsをベースに構築しています。

フロントエンド側は、最初にBoltを使用してサイトのプロトタイプを作成しました。一方で、Boltの出力ではコンポーネントの分割が不十分であったり、意図しないクライアントコンポーネント化が発生し、後からコードを改善・拡張する際に問題が生じる状態でした。そこで、Storybookを導入し、Atomic Designをもとにディレクトリ構成やコンポーネント設計を全面的に見直しました。
さらに、shadcn/uiを拡張したMagic UIを活用し、全体のUIデザインのブラッシュアップを行ったほか、ダークモードと多言語対応も実装しました。ただ、Next.jsRSCで動的に言語を切り替える仕組みをStorybook側で再現するのが予想以上に複雑で、4時間ほど費やしてしまいました(実際はやる必要があったか微妙なところでしたが……)。

Chromaticにデプロイしました👇 https://67df1188f395761ad4884e5c-oqzryofyob.chromatic.com/

バックエンド側では認証処理と、Stripeの決済完了を通知するWebhookを除き、全てのAPIHonoで構築しています。これは独立したサーバーとして立てているわけではなく、Next.jsAPI Routesにマウントする形で利用しています。この手法により、Zodを用いたバリデーションやドキュメント生成が簡単に行えるほか、HonoRPC機能を活用して型定義をフロントエンドとサーバー間で共有できるようになっています。   認証には、従来ハッカソン等でよく利用していたAuth.js(旧NextAuth.js)ではフレームワークや認証方法に制約があるため、より柔軟なBetter Authを採用しました。ORMについても、普段使い慣れているPrismaではなく、独自の記法が少なく型定義をリアルタイムで生成できるDrizzleを使用しています。

✨ アプリ側

Tauriをベースに構築しています。

⚛️ フロントエンド側

フロントエンド側は主に2つのタスクを担当しています。Tauriが提供するAPIを利用して画像・動画のダウンロード機能やメニューバーへのアイコン表示を実装すること、そしてFabric.jsを用いた画像・動画の編集画面を作成することです。これらのタスクを処理する過程で発生した辛みを3つ紹介します。

GPTがTauri1.0のコードしか出力してこない

最初はAIが次々とコードを生成してくれる様子を見て「意外といけるな」と思っていました。しかし、最近Tauriのバージョンが2系統にアップデートされており、GPTはこれに全く対応できていないことが判明しました。そのため、GPTが生成した1系統のコードを、ひたすら2系統に書き換える作業に追われることになりました。

Permissonの記述がドキュメントにない

Tauriでは、OSのさまざまな機能を利用するために権限の設定をする必要があります。この設定自体はシンプルで、default.json に必要な機能を書くだけなのですが、公式ドキュメントには具体的にどの機能にどの権限が必要なのかという情報が記載されておらず、自分でエディタの予測機能を頼りに適切な設定を探るという手探りの作業が必要でした💦

Safariのv16.1ではTailwind CSS v4が一部機能しない

この原因は、Tailwind CSS v4から新しくサポートされた色空間OKLCHにありました。どうやら、Safariのバージョン16.1ではこのOKLCHがサポートされていないらしく、Mac OSをアップデートするか、Tailwind CSSのバージョンを下げるかの二択で悩みました。結局、バージョンをv3へダウングレードし、サイト側はv4、アプリ側はv3という状況になってしまいました。

⚛️ Rust側

バックエンド側はRustで実装しており、大きく2つの機能を提供しています:「画面録画」と「カーソルの座標取得」です。

画面録画

画面録画にはFFmpegを使用しました。当初、適切な画面収録用のライブラリを見つけられなかったため、FFmpegのバイナリをプロジェクトにダウンロードしCLIツールとして実行する形で実装しました。(後になって、FFmpeg-sysのようなFFIを利用する方法があることを知りました。)録画した動画は/tmp/以下にタイムスタンプ付きのファイル名で保存し、そのファイルパスをフロントエンド側に返すことで、プレビュー機能を提供しています。

カーソルの座標取得

カーソル座標の取得には、rdevクレートを使用しました。ただし、このクレートのlistenメソッドで座標を取得する処理はブロッキングであり、プロセスを終了させる以外に停止させる方法がありませんでした。 そこで、座標取得のコードを別の実行ファイルとして分離し、それをプロセスとして起動する形で対応しました。取得した座標データは標準出力から受け取り、整形したうえでフロントエンド側に返しています。

インフラ

image

インフラ構築では、LPサイトのデプロイを目的としてECSを中心としたアーキテクチャを採用し、インスタンスにはFargateを使用しました。HTTPS接続を有効にするため、Route53ACMを利用しています。データベースにはRDSを採用し、DBMSとしてはPostgreSQLを用いています。RDSへはECS内で動作するアプリケーションから接続します。

Fargateのみではアクセスログやエラーログを確認できず、デプロイ失敗時の原因特定が困難でした。そのため、CloudWatchを導入し、ECS内のコンテナのログをCloudWatchへ転送することで、コンテナの状態監視とログ管理を可能にしました。

RDSRoute53ACM以外のAWSリソースはTerraformを使って構築しています。また、複数人でインフラを管理していたため、環境の差異がインフラに影響を与えないよう、Terraformの状態管理ファイル(tfstate)をS3に格納し、共有管理できるようにしました。

GitHubリポジトリ

ネッシー

@y_ya