feat: Renamen functionaliteit aangepast
This commit is contained in:
@@ -0,0 +1,164 @@
|
|||||||
|
# UI_F2_RENAME_V1
|
||||||
|
|
||||||
|
## 1. Doel
|
||||||
|
|
||||||
|
`Rename` moet een expliciete eigen UI-flow krijgen via `F2`, los van de bestaande `F6` move-flow.
|
||||||
|
|
||||||
|
Waarom:
|
||||||
|
- `Rename` is conceptueel een andere actie dan `Move`
|
||||||
|
- een eigen `F2`-flow sluit beter aan op klassieke file-manager bediening
|
||||||
|
- het maakt de UI voorspelbaarder: `F2` = naam wijzigen, `F6` = verplaatsen
|
||||||
|
- het vereenvoudigt later de verdere scheiding tussen rename en move in de UI, zonder backendwijziging
|
||||||
|
|
||||||
|
Bestaande context:
|
||||||
|
- rename-functionaliteit bestaat backendmatig al via het bestaande rename-endpoint
|
||||||
|
- move-functionaliteit bestaat backendmatig al via het bestaande move-endpoint
|
||||||
|
- `F6`/move-flow bestaat al in de UI
|
||||||
|
- in deze stap moet dus niets nieuws in backend of API worden ontworpen; alleen een nette eigen rename-flow in de frontend
|
||||||
|
|
||||||
|
## 2. Scope
|
||||||
|
|
||||||
|
In scope voor v1:
|
||||||
|
- exact 1 file geselecteerd
|
||||||
|
- exact 1 directory geselecteerd
|
||||||
|
- `F2` keyboard shortcut
|
||||||
|
- `Rename`-knop in de functiebalk
|
||||||
|
- compacte rename-popup
|
||||||
|
|
||||||
|
Out of scope:
|
||||||
|
- batch rename
|
||||||
|
- rename van meerdere selectie-items
|
||||||
|
- herontwerp van `F6`
|
||||||
|
- backendwijzigingen
|
||||||
|
- nieuwe dependencies
|
||||||
|
|
||||||
|
## 3. Gewenst gedrag
|
||||||
|
|
||||||
|
`F2` en de bestaande `Rename`-knop moeten exact dezelfde UI-flow gebruiken.
|
||||||
|
|
||||||
|
Voorstel:
|
||||||
|
- `F2` keyboard shortcut activeert de rename-flow
|
||||||
|
- `Rename`-knop in functiebalk activeert exact dezelfde flow
|
||||||
|
- beide openen een compacte rename-popup
|
||||||
|
|
||||||
|
Popup-eigenschappen:
|
||||||
|
- titel: `Rename`
|
||||||
|
- toont context van het geselecteerde item
|
||||||
|
- invoerveld bevat alleen de huidige naam
|
||||||
|
- dus niet het volledige pad
|
||||||
|
- focus direct in het invoerveld
|
||||||
|
- tekst vooraf geselecteerd zodat overschrijven snel kan
|
||||||
|
- `Enter` bevestigt
|
||||||
|
- `Escape` annuleert
|
||||||
|
- `X` rechtsboven sluit de popup
|
||||||
|
|
||||||
|
Belangrijk semantisch verschil met `F6`:
|
||||||
|
- `F2 Rename` werkt op naamniveau binnen dezelfde parent
|
||||||
|
- de popup toont en bewerkt alleen de naam
|
||||||
|
- geen destination pad, geen implicit move-semantiek
|
||||||
|
|
||||||
|
## 4. Validatie
|
||||||
|
|
||||||
|
Frontendvalidatie in v1 moet klein blijven en vooral duidelijke UX geven. De backend blijft de bron van waarheid.
|
||||||
|
|
||||||
|
Frontend moet minimaal blokkeren of afvangen:
|
||||||
|
- lege naam
|
||||||
|
- ongewijzigde naam
|
||||||
|
- namen met `/`
|
||||||
|
- namen die triviaal ongeldig zijn zoals `.` of `..`
|
||||||
|
|
||||||
|
Aanpak:
|
||||||
|
- lichte pre-validatie in de popup voor snelle feedback
|
||||||
|
- daarna altijd het bestaande rename-endpoint gebruiken
|
||||||
|
- backend-validatie blijft leidend voor definitieve afhandeling
|
||||||
|
|
||||||
|
Geen nieuwe rename-semantiek ontwerpen:
|
||||||
|
- geen padbewerkingen in de popup
|
||||||
|
- geen move-achtige fallback
|
||||||
|
- geen root- of parent-wijziging
|
||||||
|
|
||||||
|
## 5. Files en directories
|
||||||
|
|
||||||
|
Exact 1 file:
|
||||||
|
- rename toegestaan
|
||||||
|
- popup opent met huidige bestandsnaam
|
||||||
|
- bevestigen gebruikt bestaande backend-rename
|
||||||
|
|
||||||
|
Exact 1 directory:
|
||||||
|
- rename toegestaan
|
||||||
|
- popup opent met huidige mapnaam
|
||||||
|
- bevestigen gebruikt bestaande backend-rename
|
||||||
|
|
||||||
|
Meerdere geselecteerde items:
|
||||||
|
- in v1 niet ondersteunen
|
||||||
|
- `F2` en `Rename` doen functioneel niets destructiefs
|
||||||
|
- aanbevolen UX: knop disabled bij `selectedItems.length !== 1`
|
||||||
|
- voor keyboardshortcut: geen actie als rename in de huidige context disabled zou zijn
|
||||||
|
|
||||||
|
Dit sluit aan op de bestaande regel dat keyboardshortcuts dezelfde enabled/disabled toestand moeten respecteren als de functiebalkknoppen.
|
||||||
|
|
||||||
|
## 6. Relatie met bestaande flows
|
||||||
|
|
||||||
|
Herbruik:
|
||||||
|
- bestaande backend rename-functionaliteit hergebruiken
|
||||||
|
- bestaande move-functionaliteit ongemoeid laten
|
||||||
|
- bestaande selectie- en active-pane-logica hergebruiken
|
||||||
|
|
||||||
|
Regels:
|
||||||
|
- `F2` en `Rename`-knop delen exact dezelfde frontendflow
|
||||||
|
- `F6` blijft ongewijzigd in deze stap
|
||||||
|
- bestaande `F6` rename/move-popup wordt niet herontworpen in deze stap
|
||||||
|
- geen dubbele implementatie van backendlogica; alleen een aparte UI-laag voor rename
|
||||||
|
|
||||||
|
Pragmatische richting:
|
||||||
|
- introduceer een aparte compacte rename-popup
|
||||||
|
- submit roept hetzelfde backend-endpoint aan als de huidige renameknop al gebruikt
|
||||||
|
- succesvolle rename refresht alleen het actieve paneel, zoals nu al logisch is voor rename
|
||||||
|
|
||||||
|
## 7. Regressierisico
|
||||||
|
|
||||||
|
Belangrijkste risico's:
|
||||||
|
- selectieflow: `F2` mag niet reageren bij ongeldige selectie
|
||||||
|
- popup-focus: paneelkeyboard mag niet doorwerken terwijl de rename-popup open is
|
||||||
|
- interactie met `F6`: geen verwarring of gedeelde state tussen rename-popup en bestaande rename/move-popup
|
||||||
|
- onbedoeld herbouwen van bestaande rename/move-logica in plaats van hergebruik
|
||||||
|
|
||||||
|
Mitigatie:
|
||||||
|
- `F2` dezelfde disabled-context laten volgen als de `Rename`-knop
|
||||||
|
- aparte popup-state voor rename, niet hergebruik van de complexere `F6` destination-popup
|
||||||
|
- bestaande backend-rename endpoint direct blijven gebruiken
|
||||||
|
- geen aanpassing aan `F6` in deze stap
|
||||||
|
|
||||||
|
## 8. Teststrategie
|
||||||
|
|
||||||
|
UI smoke/regressietests:
|
||||||
|
- functiebalk bevat `Rename` met `F2`-hint
|
||||||
|
- rename-popupcontainer aanwezig in HTML
|
||||||
|
- rename-popup bevat invoerveld en sluitknop
|
||||||
|
- `F2` wiring aanwezig in frontendcode
|
||||||
|
- bestaande `F6` wiring blijft aanwezig
|
||||||
|
|
||||||
|
Handmatige validatie:
|
||||||
|
- exact 1 file geselecteerd -> `F2` opent rename-popup met alleen naam
|
||||||
|
- exact 1 directory geselecteerd -> `F2` opent rename-popup met alleen naam
|
||||||
|
- `Enter` bevestigt
|
||||||
|
- `Escape` sluit
|
||||||
|
- `X` sluit
|
||||||
|
- meerdere selectie -> `F2` doet niets / rename blijft disabled
|
||||||
|
- succesvolle rename refresht actief paneel
|
||||||
|
- `F6` move-flow blijft ongewijzigd werken
|
||||||
|
|
||||||
|
## 9. Aanbeveling
|
||||||
|
|
||||||
|
Aanbevolen v1-richting met laag regressierisico:
|
||||||
|
- voeg een aparte compacte rename-popup toe voor `F2` en de `Rename`-knop
|
||||||
|
- werk alleen met de naam van exact 1 geselecteerd item
|
||||||
|
- gebruik het bestaande backend rename-endpoint zonder nieuwe semantiek
|
||||||
|
- laat `F2` en de `Rename`-knop exact dezelfde flow delen
|
||||||
|
- laat `F6` volledig ongemoeid in deze stap
|
||||||
|
|
||||||
|
Waarom dit de veiligste richting is:
|
||||||
|
- duidelijke scheiding tussen rename en move in de UI
|
||||||
|
- minimaal risico op regressie in bestaande move-flow
|
||||||
|
- geen backendwerk nodig
|
||||||
|
- sluit goed aan op klassieke file-manager verwachtingen
|
||||||
Binary file not shown.
Binary file not shown.
@@ -38,7 +38,7 @@ class UiSmokeGoldenTest(unittest.TestCase):
|
|||||||
self.assertIn('id="right-items"', body)
|
self.assertIn('id="right-items"', body)
|
||||||
self.assertIn('id="function-bar"', body)
|
self.assertIn('id="function-bar"', body)
|
||||||
self.assertIn('id="settings-btn"', body)
|
self.assertIn('id="settings-btn"', body)
|
||||||
self.assertIn('id="rename-placeholder-btn"', body)
|
self.assertIn('id="rename-btn"', body)
|
||||||
self.assertIn('id="view-btn"', body)
|
self.assertIn('id="view-btn"', body)
|
||||||
self.assertIn('id="edit-btn"', body)
|
self.assertIn('id="edit-btn"', body)
|
||||||
self.assertIn("F1", body)
|
self.assertIn("F1", body)
|
||||||
@@ -51,6 +51,9 @@ class UiSmokeGoldenTest(unittest.TestCase):
|
|||||||
self.assertIn("F8", body)
|
self.assertIn("F8", body)
|
||||||
self.assertIn('id="viewer-modal"', body)
|
self.assertIn('id="viewer-modal"', body)
|
||||||
self.assertIn('id="settings-modal"', body)
|
self.assertIn('id="settings-modal"', body)
|
||||||
|
self.assertIn('id="rename-popup"', body)
|
||||||
|
self.assertIn('id="rename-input"', body)
|
||||||
|
self.assertIn('id="rename-apply-btn"', body)
|
||||||
self.assertIn('id="settings-general-tab"', body)
|
self.assertIn('id="settings-general-tab"', body)
|
||||||
self.assertIn('id="settings-logs-tab"', body)
|
self.assertIn('id="settings-logs-tab"', body)
|
||||||
self.assertIn('id="settings-logs-list"', body)
|
self.assertIn('id="settings-logs-list"', body)
|
||||||
@@ -77,20 +80,16 @@ class UiSmokeGoldenTest(unittest.TestCase):
|
|||||||
|
|
||||||
ordered_ids = [
|
ordered_ids = [
|
||||||
'id="settings-btn"',
|
'id="settings-btn"',
|
||||||
'id="rename-placeholder-btn"',
|
'id="rename-btn"',
|
||||||
'id="view-btn"',
|
'id="view-btn"',
|
||||||
'id="edit-btn"',
|
'id="edit-btn"',
|
||||||
'id="copy-btn"',
|
'id="copy-btn"',
|
||||||
'id="move-btn"',
|
'id="move-btn"',
|
||||||
'id="rename-btn"',
|
|
||||||
'id="mkdir-btn"',
|
'id="mkdir-btn"',
|
||||||
'id="delete-btn"',
|
'id="delete-btn"',
|
||||||
]
|
]
|
||||||
positions = [body.index(marker) for marker in ordered_ids]
|
positions = [body.index(marker) for marker in ordered_ids]
|
||||||
self.assertEqual(positions, sorted(positions))
|
self.assertEqual(positions, sorted(positions))
|
||||||
rename_placeholder_index = body.index('id="rename-placeholder-btn"')
|
|
||||||
disabled_index = body.index("disabled", rename_placeholder_index)
|
|
||||||
self.assertLess(rename_placeholder_index, disabled_index)
|
|
||||||
|
|
||||||
def test_ui_static_assets_are_present_and_mapped(self) -> None:
|
def test_ui_static_assets_are_present_and_mapped(self) -> None:
|
||||||
mount = self._ui_mount()
|
mount = self._ui_mount()
|
||||||
@@ -106,6 +105,9 @@ class UiSmokeGoldenTest(unittest.TestCase):
|
|||||||
self.assertIn('if (event.key === "F1") {', app_js)
|
self.assertIn('if (event.key === "F1") {', app_js)
|
||||||
self.assertIn('if (event.key === "F2") {', app_js)
|
self.assertIn('if (event.key === "F2") {', app_js)
|
||||||
self.assertIn('function openSettings(tab = "general")', app_js)
|
self.assertIn('function openSettings(tab = "general")', app_js)
|
||||||
|
self.assertIn('function openRenamePopup()', app_js)
|
||||||
|
self.assertIn('document.getElementById("rename-btn").onclick = openRenamePopup;', app_js)
|
||||||
|
self.assertIn('return triggerActionButton("rename-btn");', app_js)
|
||||||
self.assertIn('await apiRequest("GET", "/api/history")', app_js)
|
self.assertIn('await apiRequest("GET", "/api/history")', app_js)
|
||||||
self.assertIn('Cross-root directory move is not supported in v1', app_js)
|
self.assertIn('Cross-root directory move is not supported in v1', app_js)
|
||||||
self.assertIn('Batch directory move is not supported in v1', app_js)
|
self.assertIn('Batch directory move is not supported in v1', app_js)
|
||||||
|
|||||||
+126
-2
@@ -34,6 +34,10 @@ let renameMoveState = {
|
|||||||
source: null,
|
source: null,
|
||||||
destination: "",
|
destination: "",
|
||||||
};
|
};
|
||||||
|
let renameState = {
|
||||||
|
source: null,
|
||||||
|
name: "",
|
||||||
|
};
|
||||||
let batchMoveState = {
|
let batchMoveState = {
|
||||||
destinationBase: "",
|
destinationBase: "",
|
||||||
count: 0,
|
count: 0,
|
||||||
@@ -147,6 +151,17 @@ function renameMoveElements() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renameElements() {
|
||||||
|
return {
|
||||||
|
overlay: document.getElementById("rename-popup"),
|
||||||
|
input: document.getElementById("rename-input"),
|
||||||
|
error: document.getElementById("rename-error"),
|
||||||
|
applyButton: document.getElementById("rename-apply-btn"),
|
||||||
|
cancelButton: document.getElementById("rename-cancel-btn"),
|
||||||
|
closeButton: document.getElementById("rename-close-btn"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function batchMoveElements() {
|
function batchMoveElements() {
|
||||||
return {
|
return {
|
||||||
overlay: document.getElementById("batch-move-popup"),
|
overlay: document.getElementById("batch-move-popup"),
|
||||||
@@ -920,7 +935,7 @@ function actionShortcutHandled(event) {
|
|||||||
return triggerActionButton("settings-btn");
|
return triggerActionButton("settings-btn");
|
||||||
}
|
}
|
||||||
if (event.key === "F2") {
|
if (event.key === "F2") {
|
||||||
return false;
|
return triggerActionButton("rename-btn");
|
||||||
}
|
}
|
||||||
if (event.key === "F3") {
|
if (event.key === "F3") {
|
||||||
return triggerActionButton("view-btn");
|
return triggerActionButton("view-btn");
|
||||||
@@ -991,6 +1006,10 @@ function isRenameMovePopupOpen() {
|
|||||||
return !renameMoveElements().overlay.classList.contains("hidden");
|
return !renameMoveElements().overlay.classList.contains("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRenamePopupOpen() {
|
||||||
|
return !renameElements().overlay.classList.contains("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
function isBatchMovePopupOpen() {
|
function isBatchMovePopupOpen() {
|
||||||
return !batchMoveElements().overlay.classList.contains("hidden");
|
return !batchMoveElements().overlay.classList.contains("hidden");
|
||||||
}
|
}
|
||||||
@@ -1076,6 +1095,38 @@ function resetRenameMoveState() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetRenameState() {
|
||||||
|
renameState = {
|
||||||
|
source: null,
|
||||||
|
name: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeRenamePopup() {
|
||||||
|
const elements = renameElements();
|
||||||
|
elements.overlay.classList.add("hidden");
|
||||||
|
elements.error.textContent = "";
|
||||||
|
elements.input.value = "";
|
||||||
|
resetRenameState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRenamePopup() {
|
||||||
|
const selectedItems = activePaneState().selectedItems;
|
||||||
|
if (selectedItems.length !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const source = selectedItems[0];
|
||||||
|
const elements = renameElements();
|
||||||
|
renameState.source = source;
|
||||||
|
renameState.name = source.name;
|
||||||
|
elements.input.value = source.name;
|
||||||
|
elements.error.textContent = "";
|
||||||
|
elements.overlay.classList.remove("hidden");
|
||||||
|
elements.input.focus();
|
||||||
|
elements.input.select();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function resetBatchMoveState() {
|
function resetBatchMoveState() {
|
||||||
batchMoveState = {
|
batchMoveState = {
|
||||||
destinationBase: "",
|
destinationBase: "",
|
||||||
@@ -1154,6 +1205,45 @@ function openF6Flow() {
|
|||||||
return openBatchMovePopup(selectedItems);
|
return openBatchMovePopup(selectedItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function submitRenamePopup() {
|
||||||
|
const elements = renameElements();
|
||||||
|
const source = renameState.source;
|
||||||
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newName = elements.input.value.trim();
|
||||||
|
elements.error.textContent = "";
|
||||||
|
if (!newName) {
|
||||||
|
elements.error.textContent = "Name is required";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newName === source.name) {
|
||||||
|
elements.error.textContent = "Name must differ from current name";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newName.includes("/")) {
|
||||||
|
elements.error.textContent = "Name cannot contain /";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newName === "." || newName === "..") {
|
||||||
|
elements.error.textContent = "Invalid name";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apiRequest("POST", "/api/files/rename", {
|
||||||
|
path: source.path,
|
||||||
|
new_name: newName,
|
||||||
|
});
|
||||||
|
closeRenamePopup();
|
||||||
|
setSelectedItem(state.activePane, null);
|
||||||
|
await loadBrowsePane(state.activePane);
|
||||||
|
setStatus(`Renamed ${source.path}`);
|
||||||
|
} catch (err) {
|
||||||
|
elements.error.textContent = err.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function submitRenameMovePopup() {
|
async function submitRenameMovePopup() {
|
||||||
const elements = renameMoveElements();
|
const elements = renameMoveElements();
|
||||||
const source = renameMoveState.source;
|
const source = renameMoveState.source;
|
||||||
@@ -1532,6 +1622,19 @@ function clearSelectionForActivePane() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyboardShortcuts(event) {
|
function handleKeyboardShortcuts(event) {
|
||||||
|
if (isRenamePopupOpen()) {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
event.preventDefault();
|
||||||
|
closeRenamePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
submitRenamePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isSettingsOpen()) {
|
if (isSettingsOpen()) {
|
||||||
if (event.key === "Escape") {
|
if (event.key === "Escape") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -1679,12 +1782,33 @@ function setupEvents() {
|
|||||||
document.getElementById("settings-btn").onclick = () => openSettings("general");
|
document.getElementById("settings-btn").onclick = () => openSettings("general");
|
||||||
document.getElementById("view-btn").onclick = openViewer;
|
document.getElementById("view-btn").onclick = openViewer;
|
||||||
document.getElementById("edit-btn").onclick = openEditor;
|
document.getElementById("edit-btn").onclick = openEditor;
|
||||||
document.getElementById("rename-btn").onclick = renameSelected;
|
document.getElementById("rename-btn").onclick = openRenamePopup;
|
||||||
document.getElementById("delete-btn").onclick = deleteSelected;
|
document.getElementById("delete-btn").onclick = deleteSelected;
|
||||||
document.getElementById("copy-btn").onclick = startCopySelected;
|
document.getElementById("copy-btn").onclick = startCopySelected;
|
||||||
document.getElementById("move-btn").onclick = openF6Flow;
|
document.getElementById("move-btn").onclick = openF6Flow;
|
||||||
document.getElementById("mkdir-btn").onclick = createFolderForActivePane;
|
document.getElementById("mkdir-btn").onclick = createFolderForActivePane;
|
||||||
|
|
||||||
|
const rename = renameElements();
|
||||||
|
rename.closeButton.onclick = closeRenamePopup;
|
||||||
|
rename.cancelButton.onclick = closeRenamePopup;
|
||||||
|
rename.applyButton.onclick = submitRenamePopup;
|
||||||
|
rename.input.onkeydown = (event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
submitRenamePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
event.preventDefault();
|
||||||
|
closeRenamePopup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rename.overlay.onclick = (event) => {
|
||||||
|
if (event.target === rename.overlay) {
|
||||||
|
closeRenamePopup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const settings = settingsElements();
|
const settings = settingsElements();
|
||||||
settings.closeButton.onclick = closeSettings;
|
settings.closeButton.onclick = closeSettings;
|
||||||
settings.generalTab.onclick = () => setSettingsTab("general");
|
settings.generalTab.onclick = () => setSettingsTab("general");
|
||||||
|
|||||||
+15
-2
@@ -68,12 +68,11 @@
|
|||||||
<div id="function-bar-meta" class="pathline compact-line">Active:<code id="active-pane-label">left</code></div>
|
<div id="function-bar-meta" class="pathline compact-line">Active:<code id="active-pane-label">left</code></div>
|
||||||
<div id="function-bar" class="toolbar compact-toolbar">
|
<div id="function-bar" class="toolbar compact-toolbar">
|
||||||
<button id="settings-btn" type="button"><span class="shortcut-hint">F1</span><span>Settings</span></button>
|
<button id="settings-btn" type="button"><span class="shortcut-hint">F1</span><span>Settings</span></button>
|
||||||
<button id="rename-placeholder-btn" type="button" disabled><span class="shortcut-hint">F2</span><span>Rename</span></button>
|
<button id="rename-btn" type="button" disabled><span class="shortcut-hint">F2</span><span>Rename</span></button>
|
||||||
<button id="view-btn" type="button" disabled><span class="shortcut-hint">F3</span><span>View</span></button>
|
<button id="view-btn" type="button" disabled><span class="shortcut-hint">F3</span><span>View</span></button>
|
||||||
<button id="edit-btn" type="button" disabled><span class="shortcut-hint">F4</span><span>Edit</span></button>
|
<button id="edit-btn" type="button" disabled><span class="shortcut-hint">F4</span><span>Edit</span></button>
|
||||||
<button id="copy-btn" type="button" disabled><span class="shortcut-hint">F5</span><span>Copy</span></button>
|
<button id="copy-btn" type="button" disabled><span class="shortcut-hint">F5</span><span>Copy</span></button>
|
||||||
<button id="move-btn" type="button" disabled><span class="shortcut-hint">F6</span><span>Move</span></button>
|
<button id="move-btn" type="button" disabled><span class="shortcut-hint">F6</span><span>Move</span></button>
|
||||||
<button id="rename-btn" type="button" disabled><span>Rename</span></button>
|
|
||||||
<button id="mkdir-btn" type="button"><span class="shortcut-hint">F7</span><span>MKdir</span></button>
|
<button id="mkdir-btn" type="button"><span class="shortcut-hint">F7</span><span>MKdir</span></button>
|
||||||
<button id="delete-btn" type="button" disabled><span class="shortcut-hint">F8</span><span>Delete</span></button>
|
<button id="delete-btn" type="button" disabled><span class="shortcut-hint">F8</span><span>Delete</span></button>
|
||||||
</div>
|
</div>
|
||||||
@@ -128,6 +127,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="rename-popup" class="popup-overlay hidden" role="dialog" aria-modal="true" aria-labelledby="rename-title">
|
||||||
|
<div class="popup-card">
|
||||||
|
<button id="rename-close-btn" class="viewer-close" type="button" aria-label="Close rename">X</button>
|
||||||
|
<h3 id="rename-title">Rename</h3>
|
||||||
|
<label for="rename-input" class="popup-label">Name</label>
|
||||||
|
<input id="rename-input" type="text" autocomplete="off">
|
||||||
|
<div id="rename-error" class="error"></div>
|
||||||
|
<div class="popup-actions">
|
||||||
|
<button id="rename-apply-btn" type="button">Rename</button>
|
||||||
|
<button id="rename-cancel-btn" type="button">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="batch-move-popup" class="popup-overlay hidden" role="dialog" aria-modal="true" aria-labelledby="batch-move-title">
|
<div id="batch-move-popup" class="popup-overlay hidden" role="dialog" aria-modal="true" aria-labelledby="batch-move-title">
|
||||||
<div class="popup-card">
|
<div class="popup-card">
|
||||||
<h3 id="batch-move-title">Batch Move</h3>
|
<h3 id="batch-move-title">Batch Move</h3>
|
||||||
|
|||||||
@@ -490,6 +490,17 @@ button:disabled {
|
|||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#rename-popup .popup-card {
|
||||||
|
width: min(520px, calc(100vw - 28px));
|
||||||
|
}
|
||||||
|
|
||||||
|
#rename-input {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.popup-actions {
|
.popup-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|||||||
Reference in New Issue
Block a user