Browse Source

Fixed bug where external links were not respected by hotkeys

maziggy 5 months ago
parent
commit
f317ead3c6

+ 46 - 20
frontend/src/components/KeyboardShortcutsModal.tsx

@@ -1,5 +1,5 @@
 import { useEffect } from 'react';
-import { X, Keyboard } from 'lucide-react';
+import { X, Keyboard, ExternalLink } from 'lucide-react';
 import { useTranslation } from 'react-i18next';
 import { Card, CardContent } from './Card';
 
@@ -9,41 +9,62 @@ interface NavItem {
   labelKey: string;
 }
 
+interface SidebarItem {
+  type: 'nav' | 'external';
+  label: string;
+  labelKey?: string;
+}
+
 interface KeyboardShortcutsModalProps {
   onClose: () => void;
   navItems?: NavItem[];
+  sidebarItems?: SidebarItem[];
 }
 
-function getShortcuts(navItems: NavItem[] | undefined, t: (key: string) => string) {
-  const navShortcuts = navItems
+function getShortcuts(
+  sidebarItems: SidebarItem[] | undefined,
+  navItems: NavItem[] | undefined,
+  t: (key: string) => string
+) {
+  // Use sidebarItems if provided (new format), otherwise fall back to navItems
+  const navShortcuts = sidebarItems
+    ? sidebarItems.slice(0, 9).map((item, index) => ({
+        keys: [String(index + 1)],
+        description: item.type === 'external'
+          ? `Open ${item.label}`
+          : `Go to ${item.labelKey ? t(item.labelKey) : item.label}`,
+        isExternal: item.type === 'external',
+      }))
+    : navItems
     ? navItems.map((item, index) => ({
         keys: [String(index + 1)],
         description: `Go to ${t(item.labelKey)}`,
+        isExternal: false,
       }))
     : [
-        { keys: ['1'], description: 'Go to Printers' },
-        { keys: ['2'], description: 'Go to Archives' },
-        { keys: ['3'], description: 'Go to Queue' },
-        { keys: ['4'], description: 'Go to Statistics' },
-        { keys: ['5'], description: 'Go to Cloud Profiles' },
-        { keys: ['6'], description: 'Go to Settings' },
+        { keys: ['1'], description: 'Go to Printers', isExternal: false },
+        { keys: ['2'], description: 'Go to Archives', isExternal: false },
+        { keys: ['3'], description: 'Go to Queue', isExternal: false },
+        { keys: ['4'], description: 'Go to Statistics', isExternal: false },
+        { keys: ['5'], description: 'Go to Cloud Profiles', isExternal: false },
+        { keys: ['6'], description: 'Go to Settings', isExternal: false },
       ];
 
   return [
     { category: 'Navigation', items: navShortcuts },
     { category: 'Archives', items: [
-      { keys: ['/'], description: 'Focus search' },
-      { keys: ['U'], description: 'Open upload modal' },
-      { keys: ['Esc'], description: 'Clear selection / blur input' },
-      { keys: ['Right-click'], description: 'Context menu on cards' },
+      { keys: ['/'], description: 'Focus search', isExternal: false },
+      { keys: ['U'], description: 'Open upload modal', isExternal: false },
+      { keys: ['Esc'], description: 'Clear selection / blur input', isExternal: false },
+      { keys: ['Right-click'], description: 'Context menu on cards', isExternal: false },
     ]},
     { category: 'K-Profiles', items: [
-      { keys: ['R'], description: 'Refresh profiles' },
-      { keys: ['N'], description: 'New profile' },
-      { keys: ['Esc'], description: 'Exit selection mode' },
+      { keys: ['R'], description: 'Refresh profiles', isExternal: false },
+      { keys: ['N'], description: 'New profile', isExternal: false },
+      { keys: ['Esc'], description: 'Exit selection mode', isExternal: false },
     ]},
     { category: 'General', items: [
-      { keys: ['?'], description: 'Show this help' },
+      { keys: ['?'], description: 'Show this help', isExternal: false },
     ]},
   ];
 }
@@ -56,9 +77,9 @@ function KeyBadge({ children }: { children: string }) {
   );
 }
 
-export function KeyboardShortcutsModal({ onClose, navItems }: KeyboardShortcutsModalProps) {
+export function KeyboardShortcutsModal({ onClose, navItems, sidebarItems }: KeyboardShortcutsModalProps) {
   const { t } = useTranslation();
-  const shortcuts = getShortcuts(navItems, t);
+  const shortcuts = getShortcuts(sidebarItems, navItems, t);
 
   // Close on Escape key
   useEffect(() => {
@@ -95,7 +116,12 @@ export function KeyboardShortcutsModal({ onClose, navItems }: KeyboardShortcutsM
                 <div className="space-y-2">
                   {section.items.map((shortcut) => (
                     <div key={shortcut.description} className="flex items-center justify-between">
-                      <span className="text-white text-sm">{shortcut.description}</span>
+                      <span className="text-white text-sm flex items-center gap-1.5">
+                        {shortcut.description}
+                        {shortcut.isExternal && (
+                          <ExternalLink className="w-3 h-3 text-bambu-gray" />
+                        )}
+                      </span>
                       <div className="flex gap-1">
                         {shortcut.keys.map((key) => (
                           <KeyBadge key={key}>{key}</KeyBadge>

+ 25 - 12
frontend/src/components/Layout.tsx

@@ -237,17 +237,25 @@ export function Layout() {
       return;
     }
 
-    // Number keys for navigation (1-9) - follows sidebar order for internal nav items only
+    // Number keys for navigation (1-9) - follows sidebar order including external links
     if (!e.metaKey && !e.ctrlKey && !e.altKey) {
       const keyNum = parseInt(e.key);
-      const internalItems = orderedSidebarIds.filter(id => !isExternalLinkId(id));
-      if (keyNum >= 1 && keyNum <= internalItems.length) {
-        const navItem = navItemsMap.get(internalItems[keyNum - 1]);
-        if (navItem) {
-          e.preventDefault();
-          navigate(navItem.to);
-          return;
+      if (keyNum >= 1 && keyNum <= orderedSidebarIds.length && keyNum <= 9) {
+        const id = orderedSidebarIds[keyNum - 1];
+        e.preventDefault();
+
+        if (isExternalLinkId(id)) {
+          // External link - navigate to iframe page
+          const linkId = id.replace('ext-', '');
+          navigate(`/external/${linkId}`);
+        } else {
+          // Internal nav item
+          const navItem = navItemsMap.get(id);
+          if (navItem) {
+            navigate(navItem.to);
+          }
         }
+        return;
       }
 
       switch (e.key) {
@@ -550,10 +558,15 @@ export function Layout() {
       {showShortcuts && (
         <KeyboardShortcutsModal
           onClose={() => setShowShortcuts(false)}
-          navItems={orderedSidebarIds
-            .filter(id => !isExternalLinkId(id))
-            .map(id => navItemsMap.get(id)!)
-            .filter(Boolean)}
+          sidebarItems={orderedSidebarIds.map(id => {
+            if (isExternalLinkId(id)) {
+              const extLink = extLinksMap.get(id);
+              return extLink ? { type: 'external' as const, label: extLink.name } : null;
+            } else {
+              const navItem = navItemsMap.get(id);
+              return navItem ? { type: 'nav' as const, label: navItem.labelKey, labelKey: navItem.labelKey } : null;
+            }
+          }).filter(Boolean) as { type: 'nav' | 'external'; label: string; labelKey?: string }[]}
         />
       )}
     </div>

+ 1 - 4
frontend/src/contexts/ThemeContext.tsx

@@ -14,10 +14,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
   const [theme, setThemeState] = useState<Theme>(() => {
     const stored = localStorage.getItem('theme') as Theme | null;
     if (stored) return stored;
-    // Check system preference
-    if (window.matchMedia('(prefers-color-scheme: light)').matches) {
-      return 'light';
-    }
+    // Default to dark theme
     return 'dark';
   });
 

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


+ 1 - 1
static/index.html

@@ -7,7 +7,7 @@
     <link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png" />
     <link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png" />
     <link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png" />
-    <script type="module" crossorigin src="/assets/index-2RQZZHMw.js"></script>
+    <script type="module" crossorigin src="/assets/index-DAltyj1x.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-BYZOEJWU.css">
   </head>
   <body>

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