Jewelry AI - ライブラリとコレクション

ライブラリが空に見える。その画面から分かることと、分からないこと

このアプリの失敗のうちいくつかは、文章になりません。まだコレクションはないと伝える「コレクション」タブ、スクロール中に増えなくなるグリッド、そしてアップロードした写真が映っているカードは、示されたエラーではなく、自分で読み取る画面です。このページでは、失敗を伝える画面と、黙ったままの画面を切り分けます。

沈黙として届く三つの失敗

このアプリでは、すべての失敗が文章になるわけではありません。三つの場所で、クライアントは不具合ではなく答えのように見える画面を描きます。二つは失敗したリクエストが報告されないため、もう一つは状態が見覚えのある画像として描かれるためです。

一つ目は「コレクション」タブです。コレクションの一覧を取得できないとき、失敗はapps/jewelry-mobile/src/features/generation/store.tsxで黙って通過し、タブは「まだコレクションがありません」という空の状態を描きます。これはコレクションを一つも持たないアカウントが見る画面と同じです。同じ失敗でも、コレクションの詳細画面は違う振る舞いをして、エラーと「再試行」を表示します。この二つの振る舞いは互いの別バージョンではなく、同じものとして読むべきではありません。

二つ目はライブラリのフィードです。最初のページが失敗すると、保持していた項目を消して「再試行」付きのエラー状態を出します。スクロール中に失敗したページは、画面上の何も変えません。その判断は分岐のすぐ隣にコメントとして書かれています(apps/jewelry-mobile/src/features/generation/use-generation-feed.ts:79)。続きのページでエラーが起きたときは既存のリストを保持する、という内容です。手元に届くのは、短いとは言わないまま短いリストです。

三つ目はサムネイルです。完了した生成は生成画像を表示し、処理中または失敗した生成はアップロードした写真を表示します。これは状態が描かれているのであって、途中結果でも、何かのプレビューでもありません。

つまりライブラリが返してくるのは、状態によって選ばれた生成ごとの一枚の画像と、描くものがないときの三つの空の状態のいずれかです。そのどれも、保存されている内容を説明していません。このページの根拠はアプリが表示するものに限られます。取得の失敗と何もないアカウントが同じ画面になるのは、まさにその画面が違いを伝えていないからです。

それぞれの画面が実際にしていること

以下のファイル名と行番号は、2026-08-18時点のjewelry-mobileクライアントから読み取ったものです。

コレクションのエラーは、ある場所では飲み込まれ、別の場所では報告されます。
apps/jewelry-mobile/src/features/generation/store.tsxはコレクション取得の失敗を黙って通過させ、「コレクション」タブは「まだコレクションがありません」という空の状態に落ちます。同じ失敗に対して、コレクションの詳細画面は明示的なエラーと「再試行」を表示します。セッションの早い段階ですでにコレクションが読み込まれていれば、メモリ上のリストはそのまま残ります。紛らわしい空のタブが、とりわけ失敗した最初の読み込みのものだと言えるのはそのためです。
フィードは最初のページのエラーでは自分を空にし、続きのページのエラーではそのまま動きません。
apps/jewelry-mobile/src/features/generation/use-generation-feed.tsは、最初のページが失敗すると項目を空にし(setItems([]))、「再試行」付きのエラー状態にします。さらに下で要求されたページについては、79行目のコメントが逆の判断を記録しています。既存のリストは保持され、何も表示されません。そのため、スクロール中に接続が切れると、エラーの付かない不完全なリストが残ります。
アクションボタンがないことが、絞り込み済みの表示と何もないアカウントを分けます。
apps/jewelry-mobile/src/app/(tabs)/library.tsxでは、空の状態は三つのうちのいずれかです。「削除済み」、絞り込んだ結果が何もない状態、そして生成が一件もない状態です。新しいビジュアルを作り始めるボタンが描画されるのは最後の一つだけで、条件はactionLabel={!isDeleted && !filtered ? t('home.newVisual') : undefined}です。絞り込み済みかどうかの判定は広く(library.tsx:499)、「お気に入り」タブでも成立します。そのため、お気に入りのない出品者には、絞り込みに一致するものがないと伝えられます。
三つの異なる結果が、同じ画像を描きます。
const thumb = job.status === 'completed' ? (job.outputUri ?? job.imageUri) : job.imageUri;という一行は、処理中の生成と失敗した生成が元の写真を表示し、出力URLが空のまま完了と記録された場合も同じ写真に戻ることを意味します。拡大表示も同じ規則を使うため(library.tsx:701)、カードを大きく開いても結果についての情報は増えません。

どの画面が失敗を伝え、どの画面が黙っているか

同じ種類の問題、つまり返ってこなかったリクエストが、起きたときにどこにいるかによって別々に伝えられます。

画面失敗したもの見えるもの
「コレクション」タブコレクションの一覧を取得できなかった「まだコレクションがありません」という空の状態。失敗は生成のストアで黙って通過する
コレクションの詳細画面同じ取得の失敗明示的なエラーと「再試行」
ライブラリのグリッド、最初の読み込みフィードの最初のページ保持していた項目が消え、「再試行」付きのエラー状態が現れる
ライブラリのグリッド、スクロール中フィードの続きのページ何も起きない。リストはそのまま残り、警告も出ない
グリッドの中のカード処理中、失敗、または出力URLが空のまま完了した生成生成画像の代わりに、アップロードした写真

自分の画面を読む

各行は、実際に目の前にあり得る画面と、そこから導かれる次の一手、そしてその一手を正しくしている挙動です。

状況選択理由
アプリを起動して最初に開いた画面で、「コレクション」タブが空になっている。結論を出す前に、自分で作ったと分かっているコレクションを開く。タブは取得の失敗を空の状態に畳み込みますが、詳細画面はその失敗を伝えて「再試行」を出します。
このセッションで先ほどはコレクションが表示されていたのに、いまは空になっている。空のタブを、読み込み失敗の手がかりとしては弱いものとして扱う。一度コレクションが読み込まれれば、アプリは持っているリストを残します。紛らわしい空の状態は、セッション最初の読み込みが失敗した場合に結び付いています。
スクロール中にグリッドへ行が追加されなくなった。見えているリストを履歴の全体として読まない。続きのページの失敗は設計上何も表示せず、既存のリストにも手を触れません。
空の状態に、新しいビジュアルを作り始めるボタンがない。なくなった作業ではなく、表示を絞り込んでいるものを探す。そのボタンは生成が本当に一件もないときだけ描かれます。「お気に入り」タブを含め、絞り込み済みの表示ではボタンは描かれません。
カードにアップロードした写真が出ている。ステータスバッジを読み、画像から失敗を推測しない。処理中、失敗、そして出力URLが空のまま完了、のいずれも元の写真に戻ります。

作業がなくなったと結論づける前に

手間の少ない順に並べた確認です。

  • どの画面が空なのかを記録する。「コレクション」タブ、コレクションの詳細画面、ライブラリのグリッドのいずれか。
  • アプリを開いてから最初の読み込みなのか、このセッションですでに一度リストが表示されていたのかを確かめる。
  • コレクションの詳細画面を開き、そこで「再試行」付きのエラーが出るかどうかを見る。
  • 空の状態にアクションボタンがあるか探す。なければ、アプリはその表示を絞り込み済みとして扱っている。
  • スクロール中に増えなくなったグリッドは、完了ではなく不完全なものとして扱う。
  • 生成が失敗したと決める前に、カードとステータスバッジを突き合わせる。

このページで決着がつかないこと

ここまでの内容はクライアント自身の挙動から読み取ったものです。以下はその境界の外側にあります。

  • 作業が保存されているのか、なくなったのか。根拠はアプリが表示する内容までで、サーバー側に何が保持されているかは含みません。そのためこのページは、データが安全だとも、失われたとも述べません。
  • ある生成がなぜ失敗したのか。失敗した生成に表示される文言は別の場所で選ばれ、専用のページで扱われます。ここでその仕組みを導き直すことはしません。
  • 絞り込みとクイックタブの挙動、つまりタブがどの条件をリセットするのか、バッジが何を数えるのか。その仕組みは別にまとめてあり、このページは、何かが行を隠しているかもしれないという結論までです。
  • フィードが一ページでどれだけ読み込むのか、どんな条件でページ送りが止められるのか。ここで述べるのは、読み込みが静かに止まり得るということだけです。
  • これらがどのくらいの頻度で起きるのか。このページには頻度、規模、性能に関する主張はありません。測定されていないためです。
  • ウェブや管理画面で何が表示されるのか。ここで説明した挙動は、APIとjewelry-mobileクライアントだけから読み取ったものです。
  • 環境設定に依存するものが本番で有効になっているかどうか。それはコードからは確かめられません。

よくある質問

「コレクション」タブに、まだコレクションはないと出ています。消えてしまったのでしょうか。

その画面ではこの質問に答えられません。コレクションの一覧の読み込みが失敗すると、失敗はapps/jewelry-mobile/src/features/generation/store.tsxで黙って通過し、タブは「まだコレクションがありません」の状態に落ちます。これはコレクションを持たないアカウントが見る画面と同じです。代わりにコレクションの詳細画面を開いてください。そちらは失敗を伝え、「再試行」を出します。紛らわしい空のタブは、とりわけセッション最初の読み込みが失敗した場合のものです。すでに読み込まれたリストは保持されるからです。サーバーに何が保存されているかは、このページで伝えられる範囲の外にあります。

スクロール中にグリッドが新しい行を読み込まなくなりました。これで全部でしょうか。

そうとは限りません。最初のページが失敗した場合はリストを消して「再試行」付きのエラーを出しますが、スクロール中に失敗したページは設計上何も表示しません。リストはそのまま残り、メッセージも出ません。この判断はapps/jewelry-mobile/src/features/generation/use-generation-feed.ts:79のコードに記録されています。短いリストは、履歴が短いことの証拠にはなりません。

カードに生成画像ではなく、アップロードした写真が出ています。その生成は失敗したのでしょうか。

失敗している場合もありますが、画像はその証明にはなりません。処理中の生成も失敗した生成も元の写真を表示し、出力URLが空で届いた完了扱いの記録も、バッジを含めて同じ画像を表示します。写真ではなくカード上の状態を読んでください。拡大表示も同じ規則を使います(apps/jewelry-mobile/src/app/(tabs)/library.tsx:701)。生成がなぜ失敗したのかは失敗メッセージ自体が答えるもので、専用のページがあります。

ライブラリに、絞り込みに一致するものはないと出ますが、生成はあるはずです。なぜでしょうか。

絞り込み済みかどうかの判定は広く、「お気に入り」タブでも成立します(apps/jewelry-mobile/src/app/(tabs)/library.tsx:499)。そのため、お気に入りを一度も付けていない出品者には、アカウントに何もない場合のメッセージではなく絞り込みのメッセージが表示されます。手がかりはアクションボタンがないことです。アプリが新しいビジュアルの作成を勧めるのは、生成が本当に一件もないときだけです。絞り込みとタブ自体の挙動は専用のページにまとめてあります。