```
# src/elements/image_card.py
import asyncio
from pathlib import Path
from typing import Optional

import flet as ft

from utils.thumbnails import get_thumbnail_async, get_thumbnail, GREY_PLACEHOLDER_B64, THUMBNAIL_SEMAPHORE


async def create_image_card(item_path: Path, on_click: callable) -> ft.Card:
    check: Optional[Path] = get_thumbnail(item_path, create=False)

    if check:
        # サムネイルが存在する場合はそのサムネイルを使用
        image = ft.Image(
            src=str(check),
            fit=ft.ImageFit.COVER,
            border_radius=8,
            left=0, right=0, top=0, bottom=0,
        )
    else:
        # サムネイルが存在しない場合はプレースホルダーを使用
        image = ft.Image(
            src_base64=GREY_PLACEHOLDER_B64,
            fit=ft.ImageFit.COVER,
            border_radius=8,
            left=0, right=0, top=0, bottom=0,
        )

    card = ft.Card(
        content=ft.Container(
            content=ft.Stack([
                image,
                ft.Container(
                    height=28,
                    bgcolor=ft.Colors.with_opacity(0.6, ft.Colors.BLACK),
                    border_radius=8,
                    left=0, right=0, bottom=0,
                ),
                ft.Container(
                    content=ft.Text(
                        item_path.name,
                        size=14,
                        color=ft.Colors.WHITE,
                        weight=ft.FontWeight.BOLD,
                        text_align=ft.TextAlign.CENTER,
                        overflow=ft.TextOverflow.ELLIPSIS,
                    ),
                    alignment=ft.alignment.bottom_center,
                    padding=4,
                    margin=ft.margin.symmetric(horizontal=10),
                    border_radius=8,
                )
            ]),
            on_click=on_click,
        ),
        elevation=2,
        shape=ft.RoundedRectangleBorder(radius=8),
    )

    # サムネイル読み込み用の関数
    async def load_thumbnail():
        async with THUMBNAIL_SEMAPHORE:
            try:
                thumbnail = await get_thumbnail_async(item_path)
                image.src = str(thumbnail)
                image.src_base64 = ""
                image.update()
            except Exception:
                pass

    # サムネイルが存在しない場合にのみサムネイル読み込みを非同期で開始
    if not check or not check.exists():
        asyncio.create_task(load_thumbnail())

    return card
```

```
# src/elements/subdir_card.py
from pathlib import Path

import flet as ft

from configs.misc_config import IMAGE_EXTS
from utils.thumbnails import get_thumbnail


def create_subdir_card(item_path: Path, on_click: callable) -> ft.Card:
    thumbs = []
    for p in sorted(item_path.iterdir()):
        if p.is_file() and p.suffix.lower() in IMAGE_EXTS:
            thumbs.append(p)
            if len(thumbs) == 4:  # 4枚見つかったら早期終了
                break

    if not thumbs:
        return ft.Card(
            content=ft.Container(
                ft.Column([
                    ft.Icon(ft.Icons.FOLDER, size=60, color=ft.Colors.BLUE_400),
                    ft.Text(item_path.name, size=14, text_align=ft.TextAlign.CENTER, overflow=ft.TextOverflow.ELLIPSIS)
                ], alignment=ft.MainAxisAlignment.CENTER, horizontal_alignment=ft.CrossAxisAlignment.CENTER),
                padding=10, on_click=on_click
            ),
            elevation=2
        )

    # thumbsが4要素に満たない場合にNoneを追加
    while len(thumbs) < 4:
        thumbs.append(None)

    grid = ft.Column([
        ft.Row([
            ft.Image(src=str(get_thumbnail(thumbs[0])), fit=ft.ImageFit.COVER, expand=True, border_radius=4) if thumbs[
                0]
            else ft.Container(expand=True),
            ft.Image(src=str(get_thumbnail(thumbs[1])), fit=ft.ImageFit.COVER, expand=True, border_radius=4) if thumbs[
                1]
            else ft.Container(expand=True),
        ], spacing=2, expand=True),
        ft.Row([
            ft.Image(src=str(get_thumbnail(thumbs[2])), fit=ft.ImageFit.COVER, expand=True, border_radius=4) if thumbs[
                2]
            else ft.Container(expand=True),
            ft.Image(src=str(get_thumbnail(thumbs[3])), fit=ft.ImageFit.COVER, expand=True, border_radius=4) if thumbs[
                3]
            else ft.Container(expand=True),
        ], spacing=2, expand=True),
    ], spacing=2, expand=True)

    stack = ft.Stack([
        ft.Container(content=grid, left=0, right=0, top=0, bottom=0, border_radius=8),
        ft.Container(height=28, bgcolor=ft.Colors.with_opacity(0.6, ft.Colors.BLACK),
                     left=0, right=0, bottom=0, border_radius=8),
        ft.Container(
            content=ft.Text(item_path.name, size=14, color=ft.Colors.WHITE,
                            weight=ft.FontWeight.BOLD, overflow=ft.TextOverflow.ELLIPSIS),
            alignment=ft.alignment.bottom_center,
            padding=4,
            margin=ft.margin.symmetric(horizontal=6),
            border_radius=8
        )
    ])

    return ft.Card(
        content=ft.Container(content=stack, padding=4, on_click=on_click),
        shape=ft.RoundedRectangleBorder(radius=8),
        elevation=2
    )
```

Python 3.12, Flet 0.28.3.
image_cardで行ってる遅延ロードをsubdir_cardにも適応して。
共通部分が生じるなら、それは別に切り出して。