Upload: one blue button, the next step (#439)

- The confirm's "Start upload" was a grey card with a faint outline while
  Resume was filled; it is filled again, and the three choices share one
  shape (Override stays plain: with files there it is the risky one).
- "Cancel" before anything started only cleared the source: it says
  "Change source" now.
- "Build the image, then upload it" was a second filled button next to
  Upload; it is plain, the alternative path.
- Add to queue is filled while the queue runs (Upload is disabled then),
  and Retry is filled after a failed upload or destination check.

7.0.2-7.0.4 were never published; the changelog ships it all as 7.0.5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ub1mez9eaVtTF8pMEFXvA9
This commit is contained in:
phantomptrandClaude Opus 5.5 committed 2026-10-10 23:11:17 -07:00
1 parent 9e17809797
commit fabd8c830c
24 files changed
+56 -39

No files matched your search

+6 -1
View File
@@ -4,7 +4,7 @@ What's new in ps5upload, written for humans.
---
## 7.0.4
## 7.0.5
- **OLED theme is back, and there's a new Peach theme.** OLED is dark on pure black, so an OLED
screen's pixels stay off; Peach is a warmer light theme with a cream window and a burnt-peach
@@ -17,6 +17,11 @@ What's new in ps5upload, written for humans.
- **7z uploads on 32-bit Android:** archives with more than 4 GiB in one block (most game
backups) failed with "dist overflow", and files over 4 GiB inside an archive were cut short.
They upload in full now.
- **Upload's buttons say what they do.** The next step is the one blue button: "Start upload"
in the confirm is blue again, Retry is blue after a failure, and Add to queue is blue while
the queue runs (Upload waits then). "Cancel" before an upload has started is now "Change
source", and "Build the image, then upload it" is no longer a second blue button competing
with Upload.
- Remote Play names the signed-in account by its slot ("Slot 7"), the same as Profile.
- **Upload: the "exfat" destination is a folder, not a conversion.** Its hint is now shown as
text (phones have no hover) and says so, and sending a game folder there warns that it will
+1 -1
View File
@@ -949,7 +949,7 @@ queue_raw_error: "خطأ خام",
upload_detected_label: "مكشاف",
upload_inspecting: "الفحص",
upload_choose_diff_source: "اختيار مصدر مختلف",
upload_cancel: "إلغاء",
upload_change_source: "تغيير المصدر",
upload_add_to_queue: "إضافة إلى قائمة الانتظار",
upload_dialog_upload_to: "الارسال إلى:",
upload_dialog_resume: "استئناف",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(খালি=মুছুন)",
upload_add_to_queue: "সারিতে যোগ করুন",
upload_add_to_queue_tooltip: "এই আপলোডটি শুরু না করে সারিতে যোগ করুন",
upload_apply_exclude_rules: "বাদ দেওয়ার নিয়ম প্রয়োগ করুন",
upload_cancel: "বাতিল করুন",
upload_change_source: "উৎস পরিবর্তন করুন",
upload_choose_diff_source: "একটি ভিন্ন উৎস বেছে নিন",
upload_dest_auto: "(ডিফল্ট — /data)",
upload_dest_card_title: "গন্তব্য",
+1 -1
View File
@@ -964,7 +964,7 @@ smp_blank_delete: "(leer = löschen)",
upload_add_to_queue: "Zur Warteschlange hinzufügen",
upload_add_to_queue_tooltip: "Diesen Upload zur Warteschlange hinzufügen, ohne ihn zu starten",
upload_apply_exclude_rules: "Ausschlussregeln anwenden",
upload_cancel: "Abbrechen",
upload_change_source: "Quelle ändern",
upload_choose_diff_source: "Andere Quelle wählen",
upload_dest_auto: "(Standard — /data)",
upload_dest_card_title: "Ziel",
+1 -1
View File
@@ -1749,7 +1749,7 @@ upload_scanning_archive_hint:
upload_disabled_scanning:
"Scanning game folder — wait for the scan to finish, then this button enables.",
upload_choose_diff_source: "Choose a different source",
upload_cancel: "Cancel",
upload_change_source: "Change source",
upload_add_to_queue: "Add to queue",
upload_now: "Upload",
upload_checking: "Checking…",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(vacío = eliminar)",
upload_add_to_queue: "Añadir a la cola",
upload_add_to_queue_tooltip: "Añadir esta subida a la cola sin iniciarla",
upload_apply_exclude_rules: "Aplicar reglas de exclusión",
upload_cancel: "Cancelar",
upload_change_source: "Cambiar origen",
upload_choose_diff_source: "Elegir una fuente diferente",
upload_dest_auto: "(predeterminado — /data)",
upload_dest_card_title: "Destino",
+1 -1
View File
@@ -1611,7 +1611,7 @@ upload_scanning_archive_hint:
upload_disabled_scanning:
"در حال اسکن پوشه‌ی بازی — منتظر پایان اسکن بمانید، سپس این دکمه فعال می‌شود.",
upload_choose_diff_source: "انتخاب منبع متفاوت",
upload_cancel: "لغو",
upload_change_source: "تغییر منبع",
upload_add_to_queue: "افزودن به صف",
upload_now: "بارگذاری",
upload_checking: "در حال بررسی…",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(vide = supprimer)",
upload_add_to_queue: "Ajouter à la file d'attente",
upload_add_to_queue_tooltip: "Ajouter cet envoi à la file d'attente sans le démarrer",
upload_apply_exclude_rules: "Appliquer les règles d'exclusion",
upload_cancel: "Annuler",
upload_change_source: "Changer de source",
upload_choose_diff_source: "Choisir une autre source",
upload_dest_auto: "(par défaut — /data)",
upload_dest_card_title: "Destination",
+1 -1
View File
@@ -964,7 +964,7 @@ smp_blank_delete: "(खाली=हटाएँ)",
upload_add_to_queue: "कतार में जोड़ें",
upload_add_to_queue_tooltip: "इस अपलोड को शुरू किए बिना कतार में जोड़ें",
upload_apply_exclude_rules: "बहिष्करण नियम लागू करें",
upload_cancel: "रद्द करें",
upload_change_source: "स्रोत बदलें",
upload_choose_diff_source: "एक अलग स्रोत चुनें",
upload_dest_auto: "(डिफ़ॉल्ट — /data)",
upload_dest_card_title: "गंतव्य",
+1 -1
View File
@@ -1553,7 +1553,7 @@ upload_scanning_archive_hint:
upload_disabled_scanning:
"Játékmappa pásztázása — várd meg, amíg a pásztázás befejeződik, utána válik elérhetővé ez a gomb.",
upload_choose_diff_source: "Válassz más forrást",
upload_cancel: "Mégse",
upload_change_source: "Forrás módosítása",
upload_add_to_queue: "Hozzáadás a várólistához",
upload_now: "Feltöltés",
upload_checking: "Ellenőrzés…",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(kosong=hapus)",
upload_add_to_queue: "Tambah ke antrean",
upload_add_to_queue_tooltip: "Tambahkan unggahan ini ke antrean tanpa memulainya",
upload_apply_exclude_rules: "Terapkan aturan pengecualian",
upload_cancel: "Batal",
upload_change_source: "Ganti sumber",
upload_choose_diff_source: "Pilih sumber yang berbeda",
upload_dest_auto: "(bawaan — /data)",
upload_dest_card_title: "Tujuan",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(vuoto = elimina)",
upload_add_to_queue: "Aggiungi alla coda",
upload_add_to_queue_tooltip: "Aggiungi questo caricamento alla coda senza avviarlo",
upload_apply_exclude_rules: "Applica regole di esclusione",
upload_cancel: "Annulla",
upload_change_source: "Cambia origine",
upload_choose_diff_source: "Scegli un'altra origine",
upload_dest_auto: "(predefinito — /data)",
upload_dest_card_title: "Destinazione",
+1 -1
View File
@@ -964,7 +964,7 @@ smp_blank_delete: "(空欄=削除)",
upload_add_to_queue: "キューに追加",
upload_add_to_queue_tooltip: "このアップロードを開始せずにキューに追加します",
upload_apply_exclude_rules: "除外ルールを適用",
upload_cancel: "キャンセル",
upload_change_source: "ソースを変更",
upload_choose_diff_source: "別のソースを選択",
upload_dest_auto: "(既定 — /data)",
upload_dest_card_title: "保存先",
+1 -1
View File
@@ -964,7 +964,7 @@ smp_blank_delete: "(공백=삭제)",
upload_add_to_queue: "대기열에 추가",
upload_add_to_queue_tooltip: "이 업로드를 시작하지 않고 대기열에 추가합니다",
upload_apply_exclude_rules: "제외 규칙 적용",
upload_cancel: "취소",
upload_change_source: "소스 변경",
upload_choose_diff_source: "다른 소스 선택",
upload_dest_auto: "(기본값 — /data)",
upload_dest_card_title: "대상",
+1 -1
View File
@@ -1359,7 +1359,7 @@ upload_scanning_archive_hint:
upload_disabled_scanning:
"Skanowanie folderu gry — poczekaj na zakończenie skanowania, aby aktywować ten przycisk.",
upload_choose_diff_source: "Wybierz inne źródło",
upload_cancel: "Anuluj",
upload_change_source: "Zmień źródło",
upload_add_to_queue: "Dodaj do kolejki",
upload_now: "Prześlij",
upload_checking: "Sprawdzanie…",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(em branco=excluir)",
upload_add_to_queue: "Adicionar à fila",
upload_add_to_queue_tooltip: "Adicionar este envio à fila sem iniciá-lo",
upload_apply_exclude_rules: "Aplicar regras de exclusão",
upload_cancel: "Cancelar",
upload_change_source: "Trocar origem",
upload_choose_diff_source: "Escolher uma origem diferente",
upload_dest_auto: "(padrão — /data)",
upload_dest_card_title: "Destino",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(пусто=удалить)",
upload_add_to_queue: "Добавить в очередь",
upload_add_to_queue_tooltip: "Добавить эту загрузку в очередь, не запуская её",
upload_apply_exclude_rules: "Применить правила исключения",
upload_cancel: "Отмена",
upload_change_source: "Сменить источник",
upload_choose_diff_source: "Выбрать другой источник",
upload_dest_auto: "(по умолчанию — /data)",
upload_dest_card_title: "Назначение",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(ว่าง=ลบ)",
upload_add_to_queue: "เพิ่มลงในคิว",
upload_add_to_queue_tooltip: "เพิ่มการอัปโหลดนี้ลงในคิวโดยไม่เริ่มทันที",
upload_apply_exclude_rules: "ใช้กฎการยกเว้น",
upload_cancel: "ยกเลิก",
upload_change_source: "เปลี่ยนต้นทาง",
upload_choose_diff_source: "เลือกแหล่งที่มาอื่น",
upload_dest_auto: "(ค่าเริ่มต้น — /data)",
upload_dest_card_title: "ปลายทาง",
+1 -1
View File
@@ -963,7 +963,7 @@ smp_blank_delete: "(boş=sil)",
upload_add_to_queue: "Kuyruğa ekle",
upload_add_to_queue_tooltip: "Bu yüklemeyi başlatmadan kuyruğa ekle",
upload_apply_exclude_rules: "Hariç tutma kurallarını uygula",
upload_cancel: "İptal",
upload_change_source: "Kaynağı değiştir",
upload_choose_diff_source: "Farklı bir kaynak seç",
upload_dest_auto: "(varsayılan — /data)",
upload_dest_card_title: "Hedef",
+1 -1
View File
@@ -965,7 +965,7 @@ smp_blank_delete: "(để trống=xóa)",
upload_add_to_queue: "Thêm vào hàng đợi",
upload_add_to_queue_tooltip: "Thêm lần tải lên này vào hàng đợi mà không bắt đầu",
upload_apply_exclude_rules: "Áp dụng quy tắc loại trừ",
upload_cancel: "Hủy",
upload_change_source: "Đổi nguồn",
upload_choose_diff_source: "Chọn nguồn khác",
upload_dest_auto: "(mặc định — /data)",
upload_dest_card_title: "Đích đến",
+1 -1
View File
@@ -970,7 +970,7 @@ smp_blank_delete: "(留空=删除)",
upload_add_to_queue: "加入队列",
upload_add_to_queue_tooltip: "将此上传加入队列但不立即开始",
upload_apply_exclude_rules: "应用排除规则",
upload_cancel: "取消",
upload_change_source: "更换来源",
upload_choose_diff_source: "选择其他来源",
upload_dest_auto: "(默认 — /data)",
upload_dest_card_title: "目标位置",
+1 -1
View File
@@ -964,7 +964,7 @@ smp_blank_delete: "(留空=刪除)",
upload_add_to_queue: "加入佇列",
upload_add_to_queue_tooltip: "將此上傳加入佇列但不立即開始",
upload_apply_exclude_rules: "套用排除規則",
upload_cancel: "取消",
upload_change_source: "更換來源",
upload_choose_diff_source: "選擇其他來源",
upload_dest_auto: "(預設 — /data)",
upload_dest_card_title: "目標位置",
@@ -93,8 +93,9 @@ export function SendAsImageCard({
},
]}
/>
{/* The alternative to the Upload button below, so not filled: one primary per screen. */}
<Button
variant="primary"
variant="secondary"
disabled={
!host ||
busyElsewhere ||
+27 -16
View File
@@ -1541,12 +1541,13 @@ function Step2Options(props: {
<ConsoleChip addr={stepHost} />
</span>
)}
{/* Nothing has started yet, so this is not a Cancel: it clears the source. */}
<button
type="button"
onClick={onClear}
className="rounded-full border shadow-[var(--edge-highlight),var(--shadow-1)] hover:bg-[var(--color-float)] border-[var(--glass-edge)] bg-[var(--color-surface-raised)] px-4 py-2 text-sm"
className={ACTION_PLAIN}
>
{tr("upload_cancel", "Cancel")}
{tr("upload_change_source", undefined, "Change source")}
</button>
{/* Add-to-queue: capture the current source + options into the
persisted upload queue without starting the transfer. The
@@ -1556,7 +1557,8 @@ function Step2Options(props: {
type="button"
onClick={() => onAddToQueue("overwrite")}
disabled={detecting || preflightBusy || !!detectError}
className="rounded-full border shadow-[var(--edge-highlight),var(--shadow-1)] hover:bg-[var(--color-float)] border-[var(--glass-edge)] bg-[var(--color-surface-raised)] px-4 py-2 text-sm disabled:opacity-50"
// While the queue runs, Upload is disabled and this is the way forward.
className={queueRunning && source.kind !== "pkg" ? ACTION_PRIMARY : ACTION_PLAIN}
title={
detecting
? tr(
@@ -1594,9 +1596,7 @@ function Step2Options(props: {
className={
// For a package, Stream & install (on its card) is the main
// action; uploading it first is the fallback.
source.kind === "pkg"
? "rounded-full border border-[var(--glass-edge)] bg-[var(--color-surface-raised)] px-6 py-2 text-sm font-medium text-[var(--color-text)] shadow-[var(--edge-highlight),var(--shadow-1)] hover:bg-[var(--color-float)] disabled:opacity-50"
: "rounded-full bg-[image:var(--accent-fill)] px-6 py-2 text-sm font-semibold text-[var(--color-accent-contrast)] shadow-[var(--accent-glow)] hover:brightness-105 disabled:opacity-50"
source.kind === "pkg" || queueRunning ? ACTION_PLAIN : ACTION_PRIMARY
}
>
{preflightBusy
@@ -1620,7 +1620,7 @@ function Step2Options(props: {
`Couldn't check the destination: ${preflightError}. Make sure the PS5 is reachable, then try again.`,
)}
action={
<Button variant="secondary" size="sm" onClick={onUpload}>
<Button variant="primary" size="sm" onClick={onUpload}>
{tr("upload_failed_retry", "Retry")}
</Button>
}
@@ -1751,6 +1751,20 @@ function MirrorToRosterButton({
);
}
/** The action row under the form: the next step filled, the rest plain. */
const ACTION_PLAIN =
"rounded-full border border-[var(--glass-edge)] bg-[var(--color-surface-raised)] px-5 py-2 text-sm font-medium text-[var(--color-text)] shadow-[var(--edge-highlight),var(--shadow-1)] hover:bg-[var(--color-float)] disabled:opacity-50";
const ACTION_PRIMARY =
"rounded-full bg-[image:var(--accent-fill)] px-6 py-2 text-sm font-semibold text-[var(--color-accent-contrast)] shadow-[var(--accent-glow)] hover:brightness-105 disabled:opacity-50";
/** The upload confirm's choices: one shape for all, the primary one filled with the accent. */
const DIALOG_OPTION =
"flex items-start gap-3 rounded-[var(--radius-card)] border p-3.5 text-left text-sm transition";
const DIALOG_PRIMARY =
"border-transparent bg-[image:var(--accent-fill)] text-[var(--color-accent-contrast)] shadow-[var(--accent-glow)] hover:brightness-105";
const DIALOG_PLAIN =
"border-[var(--glass-edge)] bg-[var(--color-surface)] hover:bg-[var(--color-surface-raised)]";
function ExistingDestinationDialog({
entryCount,
dest,
@@ -1807,7 +1821,7 @@ function ExistingDestinationDialog({
<button
type="button"
onClick={onResume}
className="rounded-full border shadow-[var(--edge-highlight),var(--shadow-1)] hover:bg-[var(--color-float)] flex items-start gap-3 border-[color-mix(in_srgb,var(--color-accent)_40%,transparent)] bg-[image:var(--accent-fill)] shadow-[var(--accent-glow)] p-3 text-left text-sm text-[var(--color-accent-contrast)] hover:opacity-90"
className={`${DIALOG_OPTION} ${DIALOG_PRIMARY}`}
>
<span className="font-medium">
{tr("upload_dialog_resume", "Resume")}
@@ -1820,15 +1834,12 @@ function ExistingDestinationDialog({
</span>
</button>
)}
{/* Start is the primary action in a clean destination (filled, like Resume);
with files already there Override is the risky one, so it stays plain. */}
<button
type="button"
onClick={onOverride}
className={
"flex items-start gap-3 rounded-[var(--radius-card)] border p-3.5 text-left text-sm transition-colors " +
(hasExisting
? "border-[var(--color-border)] bg-[var(--color-surface)] hover:bg-[var(--color-surface-raised)]"
: "border-[color-mix(in_srgb,var(--color-accent)_45%,transparent)] bg-[var(--color-surface-raised)] shadow-[var(--edge-highlight),var(--shadow-1)] ring-1 ring-[color-mix(in_srgb,var(--color-accent)_30%,transparent)]")
}
className={`${DIALOG_OPTION} ${hasExisting ? DIALOG_PLAIN : DIALOG_PRIMARY}`}
>
<span className="font-medium">
{hasExisting
@@ -1849,7 +1860,7 @@ function ExistingDestinationDialog({
<button
type="button"
onClick={onCancel}
className="rounded-full border shadow-[var(--edge-highlight),var(--shadow-1)] hover:bg-[var(--color-float)] flex items-start gap-3 border-[var(--glass-edge)] bg-[var(--color-surface)] p-3 text-left text-sm"
className={`${DIALOG_OPTION} ${DIALOG_PLAIN}`}
>
<span className="font-medium">
{tr("upload_dialog_cancel", "Cancel")}
@@ -2248,7 +2259,7 @@ function TransferStatus({ onRetry }: { onRetry: () => void }) {
onDismiss={clearPhase}
action={
<Button
variant="secondary"
variant="primary"
size="sm"
// The upload that failed, not whatever the form holds now (it may
// have moved on to another source or console since).