Explorar el Código

fix(camera): show diagnostic in window-mode camera page (#1395)

  The #1395 camera-diagnostic follow-up (stethoscope control-bar icon,
  Diagnose button in the stream-error state, CameraDiagnoseModal) shipped
  wired into EmbeddedCameraViewer.tsx only — the embedded camera mode.
  CameraPage.tsx, the standalone window opened at /camera/{id} when
  camera_view_mode is "window" (the default), never got it.

  A reporter on window mode could not see the stethoscope regardless of
  container rebuilds or cache clears: the camera.diagnose strings are in
  the bundle but come from EmbeddedCameraViewer, which never renders in
  window mode. Switching to overlay mode surfaced it instantly.

  Port the diagnostic into CameraPage.tsx: Stethoscope control-bar button
  between Refresh and Fullscreen, a Diagnose button next to Retry in the
  streamError block, and the CameraDiagnoseModal render. No new i18n keys
  — camera.diagnose.* already exist in all 9 locales. The backend
  per-model camera-profile fix from the same issue is view-mode-agnostic
  and unaffected.
maziggy hace 6 días
padre
commit
51b0d28b05

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 0
CHANGELOG.md


+ 11 - 0
frontend/src/__tests__/pages/CameraPage.test.tsx

@@ -125,6 +125,17 @@ describe('CameraPage', () => {
         expect(document.body).toBeInTheDocument();
       });
     });
+
+    it('shows the camera diagnostic (stethoscope) button in the control bar (#1395)', async () => {
+      // The diagnostic shipped wired into the embedded viewer only; window mode
+      // (this page) was missing it. The control-bar button must be present here.
+      renderCameraPage(1);
+
+      await waitFor(() => {
+        expect(screen.getByText('X1 Carbon')).toBeInTheDocument();
+      });
+      expect(screen.getByTitle('Diagnose')).toBeInTheDocument();
+    });
   });
 
   describe('stream token handling (#979)', () => {

+ 32 - 7
frontend/src/pages/CameraPage.tsx

@@ -2,13 +2,14 @@ import { useState, useEffect, useRef, useCallback } from 'react';
 import { useParams, useSearchParams } from 'react-router-dom';
 import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 import { useTranslation } from 'react-i18next';
-import { RefreshCw, AlertTriangle, Camera, Maximize, Minimize, WifiOff, ZoomIn, ZoomOut } from 'lucide-react';
+import { RefreshCw, AlertTriangle, Camera, Maximize, Minimize, WifiOff, ZoomIn, ZoomOut, Stethoscope } from 'lucide-react';
 import { api, getAuthToken, getStreamToken, withStreamToken } from '../api/client';
 import { useToast } from '../contexts/ToastContext';
 import { useAuth } from '../contexts/AuthContext';
 import { useStreamTokenSync } from '../hooks/useCameraStreamToken';
 import { ChamberLight } from '../components/icons/ChamberLight';
 import { SkipObjectsModal, SkipObjectsIcon } from '../components/SkipObjectsModal';
+import { CameraDiagnoseModal } from '../components/CameraDiagnoseModal';
 
 const MAX_RECONNECT_ATTEMPTS = 5;
 const INITIAL_RECONNECT_DELAY = 2000; // 2 seconds
@@ -40,6 +41,7 @@ export function CameraPage() {
 
   const [streamMode, setStreamMode] = useState<'stream' | 'snapshot'>('stream');
   const [showSkipObjectsModal, setShowSkipObjectsModal] = useState(false);
+  const [showDiagnoseModal, setShowDiagnoseModal] = useState(false);
   const [streamError, setStreamError] = useState(false);
   const [streamLoading, setStreamLoading] = useState(true);
   const [imageKey, setImageKey] = useState(Date.now());
@@ -679,6 +681,13 @@ export function CameraPage() {
           >
             <RefreshCw className={`w-4 h-4 text-bambu-gray ${isDisabled ? 'animate-spin' : ''}`} />
           </button>
+          <button
+            onClick={() => setShowDiagnoseModal(true)}
+            className="p-1.5 hover:bg-bambu-dark-tertiary rounded"
+            title={t('camera.diagnose.button')}
+          >
+            <Stethoscope className="w-4 h-4 text-bambu-gray" />
+          </button>
           <button
             onClick={toggleFullscreen}
             className="p-1.5 hover:bg-bambu-dark-tertiary rounded"
@@ -741,12 +750,20 @@ export function CameraPage() {
                 <p className="text-xs text-bambu-gray mb-4 max-w-md">
                   {t('camera.cameraUnavailableDesc')}
                 </p>
-                <button
-                  onClick={refresh}
-                  className="px-4 py-2 bg-bambu-green text-white rounded hover:bg-bambu-green/80 transition-colors"
-                >
-                  {t('camera.retry')}
-                </button>
+                <div className="flex gap-2 justify-center">
+                  <button
+                    onClick={refresh}
+                    className="px-4 py-2 bg-bambu-green text-white rounded hover:bg-bambu-green/80 transition-colors"
+                  >
+                    {t('camera.retry')}
+                  </button>
+                  <button
+                    onClick={() => setShowDiagnoseModal(true)}
+                    className="px-4 py-2 bg-bambu-dark border border-bambu-dark-tertiary text-bambu-gray hover:text-white rounded transition-colors"
+                  >
+                    {t('camera.diagnose.button')}
+                  </button>
+                </div>
               </div>
             </div>
           )}
@@ -802,6 +819,14 @@ export function CameraPage() {
         isOpen={showSkipObjectsModal}
         onClose={() => setShowSkipObjectsModal(false)}
       />
+      {/* Camera diagnostic modal — stethoscope icon + error-state Diagnose button (#1395) */}
+      {showDiagnoseModal && (
+        <CameraDiagnoseModal
+          printerId={id}
+          printerName={printer?.name || null}
+          onClose={() => setShowDiagnoseModal(false)}
+        />
+      )}
     </div>
   );
 }

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
static/assets/index-DuJrYPHG.js


+ 1 - 1
static/index.html

@@ -26,7 +26,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-B7aP7po9.js"></script>
+    <script type="module" crossorigin src="/assets/index-DuJrYPHG.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-CBuiHfeb.css">
   </head>
   <body>

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio