最初は、履歴書を載せるためのウェブサイトを作りたかっただけだ。その方がクールで便利だと思ったからだ。しかし、チャットボットやトップページ、そしてこの技術紹介ページを追加していくうちに、このサイトは本当に私自身の能力を示す作品へと進化した!最も重要なのは、この過程で多くのことを学んだということだ!
右下のインタラクティブな小さな生物は、私がとても熱中したミニプロジェクトだ。アイトラッキング(視線追跡)のアルゴリズムと、滑らかな液体の変形効果を完成させるために数日間を費やした。しかし、ユーザーがリンクをクリックするたびにページがリロードされ、これらの生物がリセットされてしまい、本来の没入感が壊れてしまった。この問題を解決するために、このサイトをシングルページアプリケーション(SPA)に変換する必要があった。
Astroは本質的にはマルチページアプリケーション(MPA)のフレームワークであるため、Astroの <ViewTransitions /> を導入したことで、ライフサイクル(lifecycle)に関連するバグが大量に発生した。クライアント側のJavaScriptが新しいページで正常にトリガーされない一方で、これらの小さな生物は**絶対に**リロードさせないようにする必要があった。私は、2つの異なるAstroディレクティブを使って異なるコンポーネントのロジックを処理することで、この問題を解決した。生物については persist タグを使用し、ページ遷移間でそのHTMLノードをシームレスに保持するようAstroに指示した。
<div id="orange-creature" transition:persist>
</div> 標準的なページインタラクション(モーダルウィンドウやホバー効果など)の場合、ユーザーが新しいルートに入るたびにスクリプトを再実行する必要がある。標準の DOMContentLoaded はAstro SPAでは機能しないため、Astroのカスタムライフサイクルフック(lifecycle hook)にロジックをラップして、ナビゲーションのたびに確実に実行されるようにした。
// This ensures the logic re-runs every time a new page is injected into the DOM
document.addEventListener('astro:page-load', () => {
const interactiveCards = document.querySelectorAll('.interactive-card');
interactiveCards.forEach(card => initHoverLogic(card));
}); 最初は、AIのバックエンドを構築するのが最も難しい部分だと思っていた。予想外なことに、現代のAPIのおかげで統合は非常にスムーズに進んだ。本当の課題は、ボットに個性を与え、私の実際のデータに基づいて回答させることだった。
私は普通のチャットボットを作るのではなく、検索拡張生成(RAG)の原則と厳密なシステムプロンプトエンジニアリングを活用した。非常に具体的なシステムプロンプトを設計し、私の履歴書データ、プロジェクト経験、そして厳格な行動規範をAIに与え、私の口調で話し、専門的な背景に関連する質問にのみ答えるようにした。フロントエンド部分では、UIとUXに重点を移した。ストリーミングされるテキストブロックを処理するためにカスタマイズされたタイプライター効果を作成し、ZAPSPLATからの正確な効果音を統合して、チャットルームに物理的なメカニカルキーボードのような感触を与えた。
英語、日本語、中国語の翻訳を手動で管理するのは、アーキテクチャ上の悪夢だった。コードベースをモジュール化し、コンテンツとコードを完全に分離する必要があったが、ファイルツリー全体を手動でリファクタリングするには何日もかかる可能性があった。
この問題を解決するために、私はローカルのAI CLIエージェントツールに頼った。厳密なアーキテクチャのブループリントを書き、エージェントにターミナルで直接、大規模で複数ファイルにまたがるリファクタリングを実行するよう指示した。AIにファイルシステムの自律的な読み書き権限を与えるのは非常にリスクが高いため、安全なDockerコンテナを構築し、その中にAIを「閉じ込め」た。このサンドボックスにより、エージェントは私のプロジェクトファイルにしかアクセスできないようになり、これがDockerベースの開発環境を学ぶきっかけにもなった。
現代のウェブアプリケーションには、優れた触覚的フィードバックが求められる。ページをスクロールする際に滑るような感覚があり、要素が自然に現れるようにしたかった。そこで、数学的な計算によるスムーズなスクロールを実現するためにLenisを実装し、複雑なスクロールトリガーアニメーションを処理するためにGSAP(GreenSock)を使用した。これらのライブラリを使うのは初めてだったので、私にとってはまさに短期集中コースのようだった。ドキュメントを読み、タイムラインの状態を管理し、Astroのビュートランジション中にアンマウントされたコンポーネントでメモリリーク(memory leaks)が発生するのを防ぐ必要があった。
著作権問題を避けるため、視覚的なアセットは自分で制作することに決めた。一時的に生成AIの使用も考えたが、最終的にはピクセルアート(Pixel Art)を学ぶことを選んだ。しかし、その難しさを完全に見くびっていた!開発者として、純粋なロジックから視覚的なUIデザインへの移行は、私が経験した中で最も険しい学習曲線だった。
私はAsepriteを使い、苦労しながら1フレームずつピクセルアートのアセットを設計し、アニメーションを作成した。これにより、スプライトシート(sprite sheets)とCSSキーフレーム(keyframe)の統合について多くを学ぶことができた。全体として、自分の技術的な背景を視覚的に一貫性のあるUIに変換すること――flexboxの制限、タイポグラフィ、そして余白のバランスをとること――は、構築プロセス全体で最も挑戦的であり、同時に最も達成感のある部分だった。
効果音の大部分は ZAPSPLAT から取得したものだ!