XFiler.RegisterCommand("standard_copy", "標準コピー (再帰的)", function () {
    // 1. 選択されているアイテムを取得
    var items = XFiler.GetSelectedItems();
    if (items.length === 0) return false;

    // 2. 確認ダイアログ
    var message = items.length + " 件のアイテムを反対側のペインへコピーしますか？";
    if (items.length === 1) message = "「" + items[0] + "」を反対側のペインへコピーしますか？";

    if (!XFiler.Confirm(message, "コピーの確認")) return false;

    // 3. 順番にコピーを実行
    var stats = { files: 0, dirs: 0 };
    for (var i = 0; i < items.length; i++) {
        copyRecursive(items[i], stats);
    }

    // 4. 結果を通知して表示を更新
    XFiler.Log(stats.files + " 個のファイル、" + stats.dirs + " 個のディレクトリをコピーしました。");
    XFiler.Refresh();
    return true;
});

/**
 * 再帰的にアイテムをコピーする内部関数
 * @param {string} relPath アクティブ/パッシブペインからの相対パス
 * @param {object} stats 統計用オブジェクト
 */
function copyRecursive(relPath, stats) {
    // アクティブペイン側の種類を確認
    if (XFiler.IsDirectory(relPath, false)) {
        // --- ディレクトリの場合 ---
        // パッシブペイン側にディレクトリを作成
        if (XFiler.CreateDirectory(relPath, true)) {
            stats.dirs++;
        }

        // アクティブペイン側のディレクトリ内を走査
        var files = XFiler.GetFiles(relPath, false);
        for (var f = 0; f < files.length; f++) {
            copyRecursive(path.join(relPath, files[f]), stats);
        }

        var subDirs = XFiler.GetDirectories(relPath, false);
        for (var d = 0; d < subDirs.length; d++) {
            copyRecursive(path.join(relPath, subDirs[d]), stats);
        }
    } else {
        // --- ファイルの場合 ---
        // アクティブからパッシブへコピー
        // 第1引数(src)はアクティブ基準、第2引数(dest)はパッシブ基準で解決される
        if (XFiler.CopyFile(relPath, relPath)) {
            stats.files++;
        }
    }
}
