Browse Source

[Fix] File rename preserves extension in File Manager (#751)

  Renaming a file in the File Manager included the extension in the
  editable text, letting users accidentally strip it and break the file.
  The rename modal now separates the base name from the extension
  (.3mf, .gcode, .gcode.3mf), showing the extension as a non-editable
  gray suffix and re-appending it on save.
maziggy 2 months ago
parent
commit
664fc86e3f

+ 1 - 0
CHANGELOG.md

@@ -25,6 +25,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **UserEmailPreference Model Not Registered** — The `UserEmailPreference` SQLAlchemy model was not imported in `models/__init__.py`, causing mapper initialization failures when the `User` model's relationship resolved the string reference before the model class was registered with Base metadata.
 - **Native Install Missing CAP_NET_BIND_SERVICE** — The `install.sh` systemd service template was missing `AmbientCapabilities=CAP_NET_BIND_SERVICE`, causing Virtual Printer proxy mode to silently fail to bind privileged ports (322, 990) on native installations.
 - **Virtual Printer Proxy A1 Diagnostics** ([#757](https://github.com/maziggy/bambuddy/issues/757)) — Added diagnostic port probing (ports 21, 80, 443) on proxy VP bind IPs to detect if BambuStudio tries to connect on ports the proxy doesn't handle. Logs a warning when an unexpected connection is detected. Helps diagnose A1/A1 Mini proxy issues where the slicer may use a different connection flow.
+- **File Rename Removes Extension** ([#751](https://github.com/maziggy/bambuddy/issues/751)) — Renaming a file in the File Manager included the file extension in the editable text, so users could accidentally remove it (e.g. renaming `bracket.gcode.3mf` to `bracket`), making the file unprintable. The rename modal now only lets users edit the base name, with the extension shown as a non-editable suffix. Reported by @fleishmaab, confirmed by @cadtoolbox.
 
 ### Added
 - **Quick Print Speed Control** ([#256](https://github.com/maziggy/bambuddy/issues/256)) — Added a print speed control badge to the printer card controls row, next to the fan status badges. Click to choose between Silent (50%), Standard (100%), Sport (124%), and Ludicrous (166%) speed presets. The badge shows the current speed percentage with a gauge icon, always visible but disabled when no print is active. Includes optimistic UI updates for instant feedback. Requested by @Sllepper.

+ 22 - 12
frontend/src/pages/FileManagerPage.tsx

@@ -126,12 +126,17 @@ interface RenameModalProps {
 }
 
 function RenameModal({ type, currentName, onClose, onSave, isLoading, t }: RenameModalProps) {
-  const [name, setName] = useState(currentName);
+  // For files, separate the extension so users can only edit the base name
+  // Handle compound extensions like .gcode.3mf
+  const fileExtension = type === 'file' ? (currentName.match(/(\.gcode\.3mf|\.3mf|\.gcode)$/i)?.[1] ?? '') : '';
+  const baseName = type === 'file' && fileExtension ? currentName.slice(0, -fileExtension.length) : currentName;
+  const [name, setName] = useState(baseName);
 
   const handleSubmit = (e: React.FormEvent) => {
     e.preventDefault();
-    if (name.trim() && name.trim() !== currentName) {
-      onSave(name.trim());
+    const fullName = type === 'file' ? name.trim() + fileExtension : name.trim();
+    if (name.trim() && fullName !== currentName) {
+      onSave(fullName);
     }
   };
 
@@ -146,20 +151,25 @@ function RenameModal({ type, currentName, onClose, onSave, isLoading, t }: Renam
             <label className="block text-sm font-medium text-white mb-1">
               {t('common.name')}
             </label>
-            <input
-              type="text"
-              value={name}
-              onChange={(e) => setName(e.target.value)}
-              className="w-full bg-bambu-dark border border-bambu-dark-tertiary rounded px-3 py-2 text-white placeholder-bambu-gray focus:outline-none focus:border-bambu-green"
-              autoFocus
-              required
-            />
+            <div className="flex items-center bg-bambu-dark border border-bambu-dark-tertiary rounded focus-within:border-bambu-green">
+              <input
+                type="text"
+                value={name}
+                onChange={(e) => setName(e.target.value)}
+                className="flex-1 bg-transparent px-3 py-2 text-white placeholder-bambu-gray focus:outline-none min-w-0"
+                autoFocus
+                required
+              />
+              {fileExtension && (
+                <span className="pr-3 text-bambu-gray text-sm select-none whitespace-nowrap">{fileExtension}</span>
+              )}
+            </div>
           </div>
           <div className="flex justify-end gap-2 pt-2">
             <Button type="button" variant="secondary" onClick={onClose}>
               {t('common.cancel')}
             </Button>
-            <Button type="submit" disabled={!name.trim() || name.trim() === currentName || isLoading}>
+            <Button type="submit" disabled={!name.trim() || name.trim() === baseName || isLoading}>
               {isLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : t('common.rename')}
             </Button>
           </div>

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-Ceor3BCc.js


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-hPK_4Ftq.css


+ 2 - 2
static/index.html

@@ -23,8 +23,8 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-HM8c6qA1.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-DS0IE7o1.css">
+    <script type="module" crossorigin src="/assets/index-Ceor3BCc.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-hPK_4Ftq.css">
   </head>
   <body>
     <div id="root"></div>

Some files were not shown because too many files changed in this diff