feat: refine file manager transfer workspace

This commit is contained in:
ItsBlurf committed 2026-07-27 13:38:40 +01:00
1 parent 9b732fc6e2
commit d333bdb61b
11 files changed
+154 -76

No files matched your search

+1
View File
@@ -9,6 +9,7 @@ __pycache__/
*.pyc
diagnostics/
output/
.playwright-cli/
screenshot.png
logs/
+9
View File
@@ -1,5 +1,14 @@
# Changelog
## v0.4.3 Alpha 3 (2026-07-27)
* Reworked the file toolbar into one compact desktop row with secondary actions
in a More menu and responsive wrapping on narrower displays
* Darkened the interface and reduced decorative color for a more restrained
file-manager appearance
* Made the direct target and clipboard transfer workflows visually distinct
while keeping both available together
## v0.4.3 Alpha 2 (2026-07-27)
* The drag-to-upload strip stays hidden until files enter the browser
+2 -2
View File
@@ -24,8 +24,8 @@ PYTHON ?= python3
# Ship / daily use must stay 5905.
WEB_PORT ?= 5905
VERSION_TAG := bfpilot-v0.4.3-alpha.2
BUILD_VERSION ?= v0.4.3-alpha.2
VERSION_TAG := bfpilot-v0.4.3-alpha.3
BUILD_VERSION ?= v0.4.3-alpha.3
LLVM_CONFIG ?= $(if $(wildcard $(PS5_PAYLOAD_SDK)/bin/prospero-llvm-config),$(PS5_PAYLOAD_SDK)/bin/prospero-llvm-config,$(CURDIR)/build-tools/llvm-config)
LLVM_BINDIR ?= $(shell "$(LLVM_CONFIG)" --bindir 2>/dev/null || dirname "$$(command -v clang 2>/dev/null || command -v clang.exe 2>/dev/null || command -v llvm-strip 2>/dev/null || command -v llvm-strip.exe 2>/dev/null || echo clang)" 2>/dev/null || echo .)
+1 -1
View File
@@ -8,7 +8,7 @@ http://<PS5_IP>:5905/
Works from the PS5 browser or any PC/phone on the same network.
Version **v0.4.3-alpha.2**. This is an alpha build; the v0.4.1 baseline was tested
Version **v0.4.3-alpha.3**. This is an alpha build; the v0.4.1 baseline was tested
on FW **11.60**.
## Payloads
+129 -60
View File
@@ -7,19 +7,19 @@
<title>BFpilot</title>
<style>
:root {
--paper: #090d12;
--panel: #111821;
--panel-2: #151f2a;
--ink: #edf4f2;
--soft: #182330;
--line: #293645;
--muted: #9aa8b6;
--deep: #34d399;
--deep-2: #5ee0aa;
--mint: #123326;
--sky: #17314a;
--gold: #3d3218;
--rose: #3b1d23;
--paper: #05070a;
--panel: #0b1016;
--panel-2: #101720;
--ink: #e3e8ed;
--soft: #121a23;
--line: #232e39;
--muted: #8593a1;
--deep: #497f63;
--deep-2: #73ad8f;
--mint: #10251c;
--sky: #142334;
--gold: #2b281c;
--rose: #2c171c;
--red: #ff8585;
--shadow: rgba(0, 0, 0, 0.45);
--focus: rgba(94, 224, 170, 0.24);
@@ -81,10 +81,10 @@
}
button.primary {
border-color: var(--deep-2);
background: var(--deep);
color: #06130e;
font-weight: 700;
border-color: #426f59;
background: #183126;
color: #c6e8d6;
font-weight: 600;
}
button.danger {
@@ -94,8 +94,9 @@
}
button.warn {
border-color: #8a6f2d;
background: var(--gold);
border-color: #4b4836;
background: #1c1c18;
color: #d5d2bd;
}
.top {
@@ -104,7 +105,7 @@
}
.top-inner {
width: min(1500px, 100%);
width: min(1600px, 100%);
margin: 0 auto;
padding: 10px 14px;
display: grid;
@@ -194,11 +195,11 @@
}
.shell {
width: min(1500px, 100%);
width: min(1600px, 100%);
margin: 0 auto;
padding: 14px;
display: grid;
grid-template-columns: 190px minmax(0, 1fr) 300px;
grid-template-columns: 174px minmax(0, 1fr) 316px;
gap: 12px;
min-height: 0;
}
@@ -322,13 +323,14 @@
.actions {
display: flex;
flex-wrap: wrap;
position: relative;
flex-wrap: nowrap;
align-items: center;
gap: 6px;
overflow-x: hidden;
overflow-y: visible;
overflow: visible;
padding: 8px 10px;
border-bottom: 1px solid var(--line);
background: #0a0f15;
}
.actions button {
@@ -340,7 +342,7 @@
.action-group {
display: inline-flex;
flex-wrap: wrap;
flex-wrap: nowrap;
align-items: center;
gap: 6px;
padding-right: 8px;
@@ -353,6 +355,54 @@
padding-right: 0;
}
.action-menu {
position: relative;
flex: 0 0 auto;
}
.action-menu summary {
min-height: 32px;
display: inline-flex;
align-items: center;
border: 1px solid var(--line);
border-radius: 6px;
background: var(--panel-2);
color: var(--ink);
padding: 0 10px;
cursor: pointer;
list-style: none;
font-size: 13px;
}
.action-menu summary::-webkit-details-marker {
display: none;
}
.action-menu[open] summary {
border-color: var(--deep-2);
}
.action-menu-panel {
position: absolute;
top: 38px;
right: 0;
z-index: 8;
width: 188px;
display: grid;
gap: 5px;
border: 1px solid var(--line);
border-radius: 7px;
background: #0b1118;
box-shadow: 0 14px 34px rgba(0, 0, 0, 0.6);
padding: 7px;
}
.action-menu-panel button {
width: 100%;
justify-content: flex-start;
text-align: left;
}
.upload-drop {
display: none;
margin: 0 12px 12px;
@@ -497,7 +547,7 @@
align-self: start;
padding: 12px;
display: grid;
gap: 10px;
gap: 9px;
}
.target h2 {
@@ -512,6 +562,18 @@
line-height: 1.4;
}
.transfer-section {
display: grid;
gap: 8px;
border-top: 1px solid var(--line);
padding-top: 10px;
}
.transfer-section:first-of-type {
border-top: 0;
padding-top: 0;
}
.clipboard-status {
border: 1px solid var(--line);
border-radius: 8px;
@@ -709,7 +771,7 @@
display: none;
}
@media (max-width: 1300px) {
@media (max-width: 1240px) {
.shell {
grid-template-columns: 132px minmax(0, 1fr);
}
@@ -794,6 +856,7 @@
.action-group {
width: 100%;
flex-wrap: wrap;
padding: 0 0 6px;
margin: 0;
border-right: 0;
@@ -890,20 +953,23 @@
<div class="actions">
<span class="action-group">
<button id="new-folder">New Folder</button>
<button id="new-text">New Text File</button>
<button id="upload-files" class="warn">Upload Files</button>
<button id="upload-folder" class="warn">Upload Folder</button>
</span>
<span class="action-group">
<button id="open-folder" title="Open the selected folder">Open</button>
<button id="rename">Rename</button>
<button id="download">Download</button>
<button id="edit-file" class="primary">Edit</button>
<button id="load-payload" class="primary" title="Send selected .elf/.bin/.js to elfldr on port 9021">Load payload</button>
<button id="extract" class="primary">Extract</button>
<button id="chmod" title="Set permissions on selected paths">chmod</button>
<button id="delete" class="danger">Delete</button>
</span>
<details class="action-menu">
<summary>More</summary>
<div class="action-menu-panel">
<button id="new-text">New Text File</button>
<button id="upload-folder" class="warn">Upload Folder</button>
<button id="chmod" title="Set permissions on selected paths">Change Permissions</button>
<button id="delete" class="danger">Delete</button>
</div>
</details>
<span id="selection" class="count">0 selected</span>
</div>
@@ -924,32 +990,35 @@
<aside class="target" aria-label="Transfer target and file clipboard">
<h2>Transfer</h2>
<p class="target-hint">Send selected items straight to a target, or stage them and paste after navigating.</p>
<div class="rail-title">Direct target</div>
<input id="target-path" class="target-path" spellcheck="false" value="/data" aria-label="Direct transfer target">
<div class="target-buttons">
<button id="copy-target" class="primary">Copy to Target</button>
<button id="move-target">Move to Target</button>
</div>
<div class="target-actions">
<button id="target-current">Use Current</button>
<button id="open-target">Open Target</button>
</div>
<div class="target-grid" id="target-grid"></div>
<p class="target-hint">Add a shortcut to make that folder available here as a transfer target.</p>
<div class="rail-title">Clipboard</div>
<div class="target-buttons">
<button id="copy" class="primary">Copy</button>
<button id="move">Move</button>
</div>
<div id="clipboard-status" class="clipboard-status">
<strong>Clipboard empty</strong>
Select items in the file list.
</div>
<div class="target-buttons">
<button id="paste" class="primary" disabled>Paste Here</button>
<button id="clear-clipboard" disabled>Clear</button>
</div>
<section class="transfer-section">
<div class="rail-title">Direct destination</div>
<input id="target-path" class="target-path" spellcheck="false" value="/data" aria-label="Direct transfer target">
<div class="target-buttons">
<button id="copy-target" class="primary">Copy to Target</button>
<button id="move-target">Move to Target</button>
</div>
<div class="target-actions">
<button id="target-current">Use Current</button>
<button id="open-target">Open Target</button>
</div>
<div class="target-grid" id="target-grid"></div>
<p class="target-hint">Select items, choose a destination, then copy or move directly.</p>
</section>
<section class="transfer-section">
<div class="rail-title">Clipboard workflow</div>
<div class="target-buttons">
<button id="copy" class="primary">Copy</button>
<button id="move">Move</button>
</div>
<div id="clipboard-status" class="clipboard-status">
<strong>Clipboard empty</strong>
Select items in the file list.
</div>
<div class="target-buttons">
<button id="paste" class="primary" disabled>Paste Here</button>
<button id="clear-clipboard" disabled>Clear</button>
</div>
</section>
<div class="detail" id="detail">
<strong>No selection</strong>
<span>Ready</span>
Binary file not shown.
BIN
View File
Binary file not shown.
+2 -2
View File
@@ -39,7 +39,7 @@ assert(html.includes('select-all-checkbox'), '#11 select all')
assert(html.includes('selectedEntries'), '#11 selection helper')
// #12 version
assert(versionH.includes('v0.4.3-alpha.2'), 'version.h v0.4.3-alpha.2')
assert(makefile.includes('v0.4.3-alpha.2'), 'Makefile v0.4.3-alpha.2')
assert(versionH.includes('v0.4.3-alpha.3'), 'version.h v0.4.3-alpha.3')
assert(makefile.includes('v0.4.3-alpha.3'), 'Makefile v0.4.3-alpha.3')
console.log('issue surface checks passed (7-12,10)')
+5 -6
View File
@@ -20,13 +20,12 @@ const transfer = read('src/transfer.c')
const pathOps = read('src/path_ops.c')
const versionH = read('src/version.h')
// --- Action bar: wrap, not horizontal-scroll-only ---
assert(/\.actions\s*\{[^}]*flex-wrap:\s*wrap/s.test(html), 'actions flex-wrap wrap')
// --- Action bar: compact at desktop, wraps at the responsive breakpoint ---
assert(/\.actions\s*\{[^}]*flex-wrap:\s*nowrap/s.test(html), 'desktop actions stay on one row')
assert(!/\.actions\s*\{[^}]*overflow-x:\s*auto/s.test(html), 'no overflow-x auto on .actions')
assert(/overflow-x:\s*hidden/.test(html), 'overflow-x hidden present')
const mediaActions = html.match(/@media[^{]+\{[\s\S]*?\.actions\s*\{([^}]*)\}/)
if (mediaActions) {
assert(!/flex-wrap:\s*nowrap/.test(mediaActions[1]), 'media .actions must not force nowrap')
assert(/flex-wrap:\s*wrap/.test(mediaActions[1]), 'responsive actions wrap')
}
// --- Single data root ---
@@ -79,7 +78,7 @@ assert(html.includes("endsWith('.7z')"), '#9')
assert(transfer.includes('chmod_request') || transfer.includes('/api/fs/chmod'), '#10')
assert(html.includes('chmodSelected') || html.includes('id="chmod"'), '#10 ui')
assert(html.includes('row-checkbox') && html.includes('select-all-checkbox'), '#11')
assert(versionH.includes('v0.4.3-alpha.2') && makefile.includes('v0.4.3-alpha.2'),
assert(versionH.includes('v0.4.3-alpha.3') && makefile.includes('v0.4.3-alpha.3'),
'current alpha version')
console.log('v0.4.3-alpha.2 separate-launcher + UX host checks passed')
console.log('v0.4.3-alpha.3 separate-launcher + UX host checks passed')
+3 -3
View File
@@ -69,9 +69,9 @@ assert(html.includes("el('open-folder').disabled"), 'Open disabled-state is sele
assert(html.includes("el('extract').disabled"), 'Extract disabled-state is selection-aware')
assert(html.includes("browser.addEventListener('dragenter'"), 'whole browser reveals drag state')
assert(html.includes("browser.addEventListener('drop'"), 'whole browser accepts dropped files')
assert(html.includes('Add a shortcut to make that folder available here as a transfer target.'),
'target panel explains how shortcuts become transfer targets')
assert(version.includes('v0.4.3-alpha.2') && makefile.includes('v0.4.3-alpha.2'),
assert(html.includes('Select items, choose a destination, then copy or move directly.'),
'direct target workflow is explained')
assert(version.includes('v0.4.3-alpha.3') && makefile.includes('v0.4.3-alpha.3'),
'alpha version is consistent')
const scriptMatch = html.match(/<script>([\s\S]*)<\/script>/)
+2 -2
View File
@@ -5,11 +5,11 @@
#pragma once
#ifndef VERSION_TAG
#define VERSION_TAG "bfpilot-v0.4.3-alpha.2"
#define VERSION_TAG "bfpilot-v0.4.3-alpha.3"
#endif
#ifndef BUILD_VERSION
#define BUILD_VERSION "v0.4.3-alpha.2"
#define BUILD_VERSION "v0.4.3-alpha.3"
#endif
#ifndef BFPILOT_SDK_PATH