Repository: sharan1303/study-chat-app
Pull request #38: [SCA-46] Add keyboard shortcuts and enhance module creation dialog
Author: login: sharan1303
Created: 2025-04-08T17:58:17Z | Merged: 2025-04-09T00:43:48Z

Introduce keyboard shortcuts for creating new chats and modules, integrate a Command Palette for improved user interaction, and update the UI for module loading and actions to enhance accessibility and clarity.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Enabled global keyboard shortcuts for actions like opening a command interface, starting new chats, and creating modules.
  - Added an enhanced module creation dialog and integrated performance insights into the main view.
  - Introduced a command palette for searching modules and chats.
  - Added new components for global resource upload and module creation dialogs.

- **Bug Fixes**
  - Improved loading state messaging and UI responsiveness based on user authentication status.

- **Style**
  - Revised text labels and headings for clarity (e.g., updating “Description” to “Content” and “Categories” to “Dashboard”).
  - Introduced a back button and enhanced button accessibility with tooltips and responsive adjustments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/app/layout.tsx b/app/layout.tsx
index 760ff95..7590e36 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -5,17 +5,22 @@ import "./globals.css";
 import localFont from "next/font/local";
 
 import ClientSidebar from "@/components/Sidebar/ClientSidebar";
+import { CommandK } from "@/components/Main/command-k";
 
 import { Providers as AppProviders } from "@/context/providers";
 import { SidebarProvider } from "@/context/sidebar-context";
 import { SessionProvider } from "@/context/session-context";
+import { KeyboardShortcutsProvider } from "@/context/keyboard-shortcuts-context";
 
 import AnonymousDataMigration from "@/components/dialogs/AnonymousDataMigration";
 import PrivacyConsentBar from "@/components/dialogs/PrivacyConsentBar";
 import Header from "@/components/Main/Header";
 import { ClerkProviderWithTheme } from "@/components/Clerk/ClerkProviderWithTheme";
+import { GlobalModuleDialog } from "@/components/dialogs/GlobalModuleCreationDialog";
+import { GlobalResourceUploadDialog } from "@/components/Main/GlobalResourceUploadDialog";
 
 import { Analytics } from "@vercel/analytics/next";
+import { SpeedInsights } from "@vercel/speed-insights/next";
 
 // Session initializer - client component
 import { SessionInitializer } from "@/components/Main/SessionInitializer";
@@ -43,7 +48,8 @@ export const viewport: Viewport = {
  * 1. AppProviders - Sets up global app configuration and utilities
  * 2. ClerkProviderWithTheme - Handles authentication and user management with theme support
  * 3. SessionProvider - Manages user session state and persistence
- * 4. SidebarProvider - Controls sidebar state (open/closed) and related functionality
+ * 4. KeyboardShortcutsProvider - Manages global keyboard shortcuts
+ * 5. SidebarProvider - Controls sidebar state (open/closed) and related functionality
  *
  * Additional Components:
  * - SessionInitializer: Client-side component that initializes session data
@@ -63,16 +69,23 @@ export default function RootLayout({
         <AppProviders>
           <ClerkProviderWithTheme>
             <SessionProvider>
-              <SidebarProvider defaultOpen={true}>
-                <ClientSidebar />
-                <main className="flex-1 pl-7 pt-0.5 relative">
-                  <Header />
-                  {children}
-                  <Analytics />
-                  <AnonymousDataMigration />
-                </main>
-                <PrivacyConsentBar />
-              </SidebarProvider>
+              <KeyboardShortcutsProvider>
+                <SidebarProvider defaultOpen={true}>
+                  <ClientSidebar />
+                  <main className="flex-1 pl-7 pt-0.5 relative">
+                    <Header />
+                    {children}
+                    <Analytics />
+                    <SpeedInsights />
+                    <AnonymousDataMigration />
+                  </main>
+
+                  <PrivacyConsentBar />
+                  <GlobalModuleDialog />
+                  <GlobalResourceUploadDialog />
+                  <CommandK />
+                </SidebarProvider>
+              </KeyboardShortcutsProvider>
               <SessionInitializer />
             </SessionProvider>
           </ClerkProviderWithTheme>
diff --git a/app/modules/[moduleName]/loading.tsx b/app/modules/[moduleName]/loading.tsx
index 150070c..711215a 100644
--- a/app/modules/[moduleName]/loading.tsx
+++ b/app/modules/[moduleName]/loading.tsx
@@ -38,7 +38,7 @@ export default function ModuleDetailsLoading() {
       <div className="space-y-6 px-3">
         {/* Description section skeleton */}
         <div className="space-y-2">
-          <h2 className="text-lg font-semibold mb-2">Description</h2>
+          <h2 className="text-lg font-semibold mb-2">Content</h2>
           <div className="text-muted-foreground p-4 rounded min-h-[158px] bg-muted">
           </div>
         </div>
diff --git a/app/modules/loading.tsx b/app/modules/loading.tsx
index 18a56bb..c96c8e3 100644
--- a/app/modules/loading.tsx
+++ b/app/modules/loading.tsx
@@ -14,7 +14,7 @@ export default function Loading() {
   return (
     <div className="flex-1 space-y-4">
       <div className="flex items-center justify-between px-3 py-3.5">
-        <h1 className="font-bold text-xl">Categories</h1>
+        <h1 className="font-bold text-xl">Dashboard</h1>
       </div>
 
       {/* Tabs with static labels */}
diff --git a/app/settings/loading.tsx b/app/settings/loading.tsx
index 79d3824..5464b2c 100644
--- a/app/settings/loading.tsx
+++ b/app/settings/loading.tsx
@@ -42,7 +42,7 @@ export default function SettingsLoading() {
                 </CardTitle>
               </CardHeader>
               <CardContent className="space-y-2">
-                {[1, 2, 3].map((i) => (
+                {[1, 2, 3, 4, 5].map((i) => (
                   <div key={i} className="flex justify-between items-center">
                     <Skeleton className="h-4 w-20" />
                     <div className="flex gap-1">
@@ -86,7 +86,7 @@ export default function SettingsLoading() {
                       <h3 className="text-lg font-medium">Email addresses</h3>
                     </div>
                     <div className="space-y-3">
-                      {[1, 2].map((i) => (
+                      {[1].map((i) => (
                         <div
                           key={i}
                           className="flex items-center justify-between"
diff --git a/app/settings/page.tsx b/app/settings/page.tsx
index d9771e6..8e01b57 100644
--- a/app/settings/page.tsx
+++ b/app/settings/page.tsx
@@ -1,6 +1,6 @@
 "use client";
 
-import { Suspense } from "react";
+import { Suspense, useEffect, useState } from "react";
 import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
 import {
   AlertDialog,
@@ -42,6 +42,7 @@ import { useRouter } from "next/navigation";
 import React, { useRef } from "react";
 import CustomUserProfile from "@/components/Settings/CustomUserProfile";
 import EditableProfileImage from "@/components/Settings/EditableProfileImage";
+import { getModifierKey } from "@/lib/utils";
 
 // Create a wrapper component for the settings content
 function SettingsContent() {
@@ -50,6 +51,12 @@ function SettingsContent() {
   const router = useRouter();
   const [isDeleteDialogOpen, setIsDeleteDialogOpen] = React.useState(false);
   const fileInputRef = useRef<HTMLInputElement>(null);
+  const [modKey, setModKey] = useState<string>("Ctrl/⌘"); // Default value for SSR
+
+  useEffect(() => {
+    // Set the correct modifier key based on the user's OS
+    setModKey(getModifierKey());
+  }, []);
 
   const handleFileImport = (event: React.ChangeEvent<HTMLInputElement>) => {
     const file = event.target.files?.[0];
@@ -226,21 +233,45 @@ function SettingsContent() {
                 <div className="flex justify-between items-center">
                   <span className="text-sm">Search</span>
                   <div className="flex gap-1">
-                    <kbd className="px-2 py-1 rounded bg-muted text-xs">⌘</kbd>
+                    <kbd className="px-2 py-1 rounded bg-muted text-xs">
+                      {modKey}
+                    </kbd>
                     <kbd className="px-2 py-1 rounded bg-muted text-xs">K</kbd>
                   </div>
                 </div>
                 <div className="flex justify-between items-center">
                   <span className="text-sm">New Chat</span>
                   <div className="flex gap-1">
-                    <kbd className="px-2 py-1 rounded bg-muted text-xs">⌘</kbd>
+                    <kbd className="px-2 py-1 rounded bg-muted text-xs">
+                      {modKey}
+                    </kbd>
+                    <kbd className="px-2 py-1 rounded bg-muted text-xs">I</kbd>
+                  </div>
+                </div>
+                <div className="flex justify-between items-center">
+                  <span className="text-sm">New Module</span>
+                  <div className="flex gap-1">
+                    <kbd className="px-2 py-1 rounded bg-muted text-xs">
+                      {modKey}
+                    </kbd>
                     <kbd className="px-2 py-1 rounded bg-muted text-xs">J</kbd>
                   </div>
                 </div>
+                <div className="flex justify-between items-center">
+                  <span className="text-sm">Upload Resource</span>
+                  <div className="flex gap-1">
+                    <kbd className="px-2 py-1 rounded bg-muted text-xs">
+                      {modKey}
+                    </kbd>
+                    <kbd className="px-2 py-1 rounded bg-muted text-xs">U</kbd>
+                  </div>
+                </div>
                 <div className="flex justify-between items-center">
                   <span className="text-sm">Toggle Sidebar</span>
                   <div className="flex gap-1">
-                    <kbd className="px-2 py-1 rounded bg-muted text-xs">⌘</kbd>
+                    <kbd className="px-2 py-1 rounded bg-muted text-xs">
+                      {modKey}
+                    </kbd>
                     <kbd className="px-2 py-1 rounded bg-muted text-xs">B</kbd>
                   </div>
                 </div>
diff --git a/components/Main/GlobalResourceUploadDialog.tsx b/components/Main/GlobalResourceUploadDialog.tsx
new file mode 100644
index 0000000..9b4d936
--- /dev/null
+++ b/components/Main/GlobalResourceUploadDialog.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { ResourceUploadDialog } from "@/components/dialogs/ResourceUploadDialog";
+
+// Event name that was defined in command-k.tsx
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+export function GlobalResourceUploadDialog() {
+  const [open, setOpen] = useState(false);
+
+  useEffect(() => {
+    // Function to handle the custom event
+    const handleOpenDialog = () => {
+      setOpen(true);
+    };
+
+    // Add event listener
+    document.addEventListener(
+      OPEN_RESOURCE_UPLOAD_DIALOG_EVENT,
+      handleOpenDialog
+    );
+
+    // Clean up event listener
+    return () => {
+      document.removeEventListener(
+        OPEN_RESOURCE_UPLOAD_DIALOG_EVENT,
+        handleOpenDialog
+      );
+    };
+  }, []);
+
+  return <ResourceUploadDialog open={open} onOpenChange={setOpen} />;
+}
diff --git a/components/Main/command-k.tsx b/components/Main/command-k.tsx
new file mode 100644
index 0000000..b9d288e
--- /dev/null
+++ b/components/Main/command-k.tsx
@@ -0,0 +1,267 @@
+"use client";
+
+import * as React from "react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import {
+  CommandDialog,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+  CommandSeparator,
+  CommandShortcut,
+} from "@/components/ui/command";
+import { DialogTitle } from "@/components/ui/dialog";
+import { api } from "@/lib/api";
+import { encodeModuleSlug, getModifierKey } from "@/lib/utils";
+
+// Custom event name for opening the module dialog
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+// Custom event name for opening the resource upload dialog
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+// Interface for module objects
+interface Module {
+  id: string;
+  name: string;
+  description?: string;
+  icon?: string;
+}
+
+// Interface for chat objects
+interface Chat {
+  id: string;
+  title: string;
+  moduleId?: string;
+  module?: {
+    id: string;
+    name: string;
+    icon?: string;
+  };
+  updatedAt?: string;
+}
+
+export function CommandK() {
+  const [open, setOpen] = useState(false);
+  const [modules, setModules] = useState<Module[]>([]);
+  const [chats, setChats] = useState<Chat[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [searchQuery, setSearchQuery] = useState("");
+  const [modKey, setModKey] = useState("Ctrl/⌘"); // Default for SSR
+  const router = useRouter();
+
+  // Set the correct modifier key based on platform
+  useEffect(() => {
+    setModKey(getModifierKey());
+  }, []);
+
+  // Fetch modules and chats when the command palette opens
+  useEffect(() => {
+    if (open) {
+      const fetchData = async () => {
+        try {
+          setLoading(true);
+
+          // Fetch modules and chats in parallel
+          const [modulesResponse, chatsResponse] = await Promise.all([
+            api.getModules(),
+            api.getChatHistory(),
+          ]);
+
+          setModules(modulesResponse.modules || []);
+          setChats(chatsResponse || []);
+        } catch (error) {
+          console.error("Error fetching data:", error);
+          setModules([]);
+          setChats([]);
+        } finally {
+          setLoading(false);
+        }
+      };
+
+      fetchData();
+    } else {
+      // Reset search when closing the dialog
+      setSearchQuery("");
+    }
+  }, [open]);
+
+  // Filter modules and chats based on search query
+  const filteredResults = React.useMemo(() => {
+    if (!searchQuery.trim()) return { modules: [], chats: [] };
+
+    const normalizedQuery = searchQuery.toLowerCase().trim();
+
+    // Filter modules - search in name
+    const filteredModules = modules.filter((module) =>
+      module.name.toLowerCase().includes(normalizedQuery)
+    );
+
+    // Filter chats - search in both title and ID
+    const filteredChats = chats.filter(
+      (chat) =>
+        chat.title.toLowerCase().includes(normalizedQuery) ||
+        chat.id.toLowerCase().includes(normalizedQuery)
+    );
+
+    return { modules: filteredModules, chats: filteredChats };
+  }, [modules, chats, searchQuery]);
+
+  useEffect(() => {
+    const down = (e: KeyboardEvent) => {
+      if ((e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey)) {
+        e.preventDefault();
+        setOpen((open) => !open);
+      }
+    };
+
+    document.addEventListener("keydown", down);
+    return () => document.removeEventListener("keydown", down);
+  }, []);
+
+  // Handler for creating a new module
+  const handleCreateModule = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the module dialog
+    // This will be caught by the GlobalModuleDialog component
+    const event = new CustomEvent(OPEN_MODULE_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for opening the resource upload dialog
+  const handleResourceUpload = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the resource upload dialog
+    const event = new CustomEvent(OPEN_RESOURCE_UPLOAD_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for creating a new chat
+  const handleCreateChat = () => {
+    setOpen(false);
+    router.push("/chat");
+  };
+
+  // Handler for navigating to a module's detail page
+  const handleModuleSelect = (moduleName: string) => {
+    setOpen(false);
+    const encodedName = encodeModuleSlug(moduleName);
+    router.push(`/modules/${encodedName}`);
+  };
+
+  // Handler for navigating to a chat
+  const handleChatSelect = (chatId: string) => {
+    setOpen(false);
+    router.push(`/chat/${chatId}`);
+  };
+
+  return (
+    <CommandDialog open={open} onOpenChange={setOpen}>
+      <DialogTitle className="sr-only">Command Menu</DialogTitle>
+      <CommandInput
+        placeholder={`Type a command or search for modules and chats... (${modKey}+K)`}
+        value={searchQuery}
+        onValueChange={setSearchQuery}
+        className="bg-transparent border-none focus:ring-0"
+      />
+      <CommandList>
+        <CommandEmpty>
+          {loading ? "Loading..." : "No results found."}
+        </CommandEmpty>
+
+        {!searchQuery && (
+          <>
+            <CommandGroup heading="Suggestions">
+              <CommandItem onSelect={handleCreateChat} value="create-new-chat">
+                <span>Create New Chat</span>
+                <CommandShortcut>{modKey}I</CommandShortcut>
+              </CommandItem>
+              <CommandItem
+                onSelect={handleCreateModule}
+                value="create-new-module"
+              >
+                <span>Create New Module</span>
+                <CommandShortcut>{modKey}J</CommandShortcut>
+              </CommandItem>
+
+              <CommandItem
+                onSelect={handleResourceUpload}
+                value="upload-resource"
+              >
+                <span>Upload Resource</span>
+                <CommandShortcut>{modKey}U</CommandShortcut>
+              </CommandItem>
+            </CommandGroup>
+
+            <CommandSeparator />
+
+            <CommandGroup heading="Navigation">
+              <CommandItem
+                value="nav-modules"
+                onSelect={() => {
+                  setOpen(false);
+                  router.push("/modules");
+                }}
+              >
+                <span>Modules & Resources dashboard</span>
+              </CommandItem>
+              <CommandItem
+                value="nav-settings"
+                onSelect={() => {
+                  setOpen(false);
+                  router.push("/settings");
+                }}
+              >
+                <span>Settings</span>
+              </CommandItem>
+            </CommandGroup>
+          </>
+        )}
+
+        {filteredResults.modules.length > 0 && (
+          <CommandGroup heading="Modules">
+            {filteredResults.modules.map((module) => (
+              <CommandItem
+                key={module.id}
+                value={`module-${module.id}-${module.name.toLowerCase()}`}
+                onSelect={() => handleModuleSelect(module.name)}
+              >
+                <span>{module.name}</span>
+              </CommandItem>
+            ))}
+          </CommandGroup>
+        )}
+
+        {filteredResults.chats.length > 0 && (
+          <CommandGroup heading="Chats">
+            {filteredResults.chats.map((chat) => (
+              <CommandItem
+                key={chat.id}
+                value={`chat-${chat.id}-${chat.title.toLowerCase()}`}
+                onSelect={() => handleChatSelect(chat.id)}
+              >
+                <div className="flex justify-between w-full items-center">
+                  <div className="flex flex-col">
+                    <span>{chat.title}</span>
+                    {chat.module && (
+                      <span className="text-xs text-muted-foreground">
+                        {chat.module.name}
+                      </span>
+                    )}
+                  </div>
+                  {chat.updatedAt && (
+                    <span className="text-xs text-muted-foreground">
+                      {new Date(chat.updatedAt).toLocaleDateString()}
+                    </span>
+                  )}
+                </div>
+              </CommandItem>
+            ))}
+          </CommandGroup>
+        )}
+      </CommandList>
+    </CommandDialog>
+  );
+}
diff --git a/components/Modules/module-actions.tsx b/components/Modules/module-actions.tsx
index 1da4da4..3e6df80 100644
--- a/components/Modules/module-actions.tsx
+++ b/components/Modules/module-actions.tsx
@@ -7,8 +7,13 @@ import { Edit, Loader2, Trash } from "lucide-react";
 import { toast } from "sonner";
 
 import { Button } from "@/components/ui/button";
-import { Dialog, DialogContent, DialogTrigger, DialogTitle } from "@/components/ui/dialog";
-import { ModuleForm } from "@/components/Modules/module-form";
+import {
+  Dialog,
+  DialogContent,
+  DialogTrigger,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import { ModuleForm } from "@/components/dialogs/ModuleForm";
 import {
   AlertDialog,
   AlertDialogAction,
@@ -111,7 +116,7 @@ export default function ModuleActions({
         onOpenChange={(open) => !open && setIsEditing(false)}
       >
         <DialogTrigger asChild>
-          <Button variant="ghost" size="icon" onClick={handleEdit}>
+          <Button variant="ghost" size="icon" onClick={handleEdit} title="Edit Module">
             <Edit className="h-4 w-4" />
           </Button>
         </DialogTrigger>
@@ -135,6 +140,7 @@ export default function ModuleActions({
           size="icon"
           onClick={() => setShowDeleteAlert(true)}
           disabled={isDeleting}
+          title="Delete Module"
         >
           {isDeleting ? (
             <Loader2 className="h-4 w-4 animate-spin" />
diff --git a/components/Modules/module-detail-page-wrapper.tsx b/components/Modules/module-detail-page-wrapper.tsx
index 3e64ced..f95a5d3 100644
--- a/components/Modules/module-detail-page-wrapper.tsx
+++ b/components/Modules/module-detail-page-wrapper.tsx
@@ -3,7 +3,7 @@
 import { useEffect, useState, useRef, useCallback } from "react";
 import { notFound, useRouter } from "next/navigation";
 import axios from "axios";
-import { Check, MessageSquare, X } from "lucide-react";
+import { Check, MessageSquare, X, ChevronLeft } from "lucide-react";
 import { decodeModuleSlug, encodeModuleSlug } from "@/lib/utils";
 import { api } from "@/lib/api";
 import { useAuth } from "@clerk/nextjs";
@@ -655,6 +655,16 @@ export default function ModuleDetailWrapper({
       <div className="flex-1 space-y-4">
         <div className="flex items-center justify-between py-2">
           <div className="flex items-center">
+            {/* Back button */}
+            <Button
+              variant="ghost"
+              size="icon"
+              onClick={() => router.back()}
+              aria-label="Go back"
+            >
+              <ChevronLeft className="h-10 w-10" />
+            </Button>
+
             {/* Module icon with popover for changing */}
             <Popover>
               <PopoverTrigger asChild>
@@ -715,7 +725,7 @@ export default function ModuleDetailWrapper({
               </div>
             ) : (
               <h1
-                className="text-xl font-bold cursor-pointer hover:bg-muted/50 px-2 py-1 rounded"
+                className="text-xl font-bold cursor-pointer hover:bg-muted/50 px-2 py-1.5 rounded"
                 onClick={() => setIsEditingTitle(true)}
               >
                 {module.name}
@@ -737,9 +747,9 @@ export default function ModuleDetailWrapper({
           </div>
         </div>
 
-        <div className="space-y-6 px-3">
+        <div className="space-y-6 px-3 mr-8">
           <div>
-            <h2 className="text-lg font-semibold mb-2">Description</h2>
+            <h2 className="text-lg font-semibold mb-2">Content</h2>
             {/* Editable description */}
             {isEditingDescription ? (
               <div className="flex flex-col gap-2" ref={descriptionEditRef}>
@@ -747,7 +757,7 @@ export default function ModuleDetailWrapper({
                   value={editDescription}
                   onChange={(e) => setEditDescription(e.target.value)}
                   className="min-h-[158px]"
-                  placeholder="Add a description..."
+                  placeholder="Give context to your agent..."
                   autoFocus
                   onKeyDown={(e) => {
                     // Save on Enter
@@ -789,8 +799,7 @@ export default function ModuleDetailWrapper({
                 className="text-muted-foreground cursor-pointer hover:bg-muted/50 p-4 rounded min-h-[158px]"
                 onClick={() => setIsEditingDescription(true)}
               >
-                {module.description ||
-                  "No description provided. Click to add one."}
+                {module.description || "Give context to your agent..."}
               </p>
             )}
           </div>
diff --git a/components/Modules/module-operations.tsx b/components/Modules/module-operations.tsx
index 60166ad..cb61f56 100644
--- a/components/Modules/module-operations.tsx
+++ b/components/Modules/module-operations.tsx
@@ -10,7 +10,7 @@ import {
   DialogTrigger,
   DialogTitle,
 } from "@/components/ui/dialog";
-import { ModuleForm } from "@/components/Modules/module-form";
+import { ModuleForm } from "@/components/dialogs/ModuleForm";
 
 interface ModuleOperationsProps {
   showLarge?: boolean;
@@ -32,14 +32,14 @@ export default function ModuleOperations({
     <Dialog open={isCreating} onOpenChange={setIsCreating}>
       <DialogTrigger asChild>
         {showLarge ? (
-          <Button size="lg" variant="outline">
-            <Plus className="h-4 w-4 mr-2" />
-            Create Module
+          <Button size="lg" variant="outline" className="whitespace-nowrap">
+            <Plus className="h-4 w-4 mr-0 sm:mr-2" />
+            <span className="hidden sm:inline">Create Module</span>
           </Button>
         ) : (
-          <Button variant="outline">
-            <Plus className="h-4 w-4 mr-2" />
-            Create Module
+          <Button variant="outline" className="whitespace-nowrap">
+            <Plus className="h-4 w-4 mr-0 sm:mr-2" />
+            <span className="hidden sm:inline">Create Module</span>
           </Button>
         )}
       </DialogTrigger>
diff --git a/components/Modules/modules-content.tsx b/components/Modules/modules-content.tsx
index d8963ca..911b0fe 100644
--- a/components/Modules/modules-content.tsx
+++ b/components/Modules/modules-content.tsx
@@ -3,12 +3,11 @@
 import React, { Suspense, useState } from "react";
 import { useAuth } from "@clerk/nextjs";
 import Link from "next/link";
-import { Search } from "lucide-react";
+import { Loader2, Search } from "lucide-react";
 import { Button } from "@/components/ui/button";
 import {
   Card,
   CardContent,
-  CardDescription,
   CardFooter,
   CardHeader,
   CardTitle,
@@ -202,7 +201,7 @@ export default function ModulesPageContent({
   // Updated ResourcesWrapper using the resources from our custom hook
   function ResourcesWrapper() {
     return (
-      <div className="mt-6">
+      <div>
         <div className="flex items-center justify-between mb-4">
           {searchQuery && (
             <p className="text-muted-foreground">
@@ -213,15 +212,33 @@ export default function ModulesPageContent({
 
         {/* Use the ResourceTable component */}
         <div className="min-h-[300px]">
-          {resourcesLoading ? (
-            <ResourceTableSkeleton showModuleColumn={true} />
-          ) : filteredResources.length === 0 && isSignedIn ? (
+          {!isSignedIn ? (
             <div className="flex flex-col items-center justify-center space-y-4 rounded-lg border border-dashed p-8 text-center">
-              <h3 className="font-medium">
-                Access your knowledge base and upload your own resources.
+              <h3 className="text-lg font-medium">
+                Sign in to view and upload resources.
               </h3>
+              <SignInButton mode="modal">
+                <Button>Sign in</Button>
+              </SignInButton>
+            </div>
+          ) : resourcesLoading ? (
+            <ResourceTableSkeleton showModuleColumn={true} />
+          ) : filteredResources.length === 0 ? (
+            <div className="flex flex-col items-center justify-center space-y-4 rounded-lg border border-dashed p-8 text-center">
+              {searchQuery ? (
+                <>
+                  <h3 className="text-xl font-medium">No resources found</h3>
+                  <p className="text-muted-foreground">
+                    Try a different search term
+                  </p>
+                </>
+              ) : (
+                <h3 className="font-medium">
+                  Access your knowledge base and upload your own resources.
+                </h3>
+              )}
             </div>
-          ) : isSignedIn ? (
+          ) : (
             <ResourceTable
               resources={filteredResources.map((resource) => ({
                 ...resource,
@@ -231,12 +248,6 @@ export default function ModulesPageContent({
               onUpdate={handleResourceUpdate}
               showModuleColumn={true}
             />
-          ) : (
-            <div className="flex flex-col items-center justify-center space-y-4 rounded-lg border border-dashed p-8 text-center">
-              <h3 className="font-medium">
-                You need to be signed in to view and upload resources.
-              </h3>
-            </div>
           )}
         </div>
       </div>
@@ -246,9 +257,9 @@ export default function ModulesPageContent({
   // Return the main UI
   return (
     <div className="flex min-h-screen w-full flex-col">
-      <div className="flex-1 space-y-4">
+      <div className="flex-1 space-y-4 mr-8">
         <div className="flex items-center justify-between px-3 py-3.5">
-          <h1 className="font-bold text-xl">Categories</h1>
+          <h1 className="font-bold text-xl">Dashboard</h1>
         </div>
 
         {/* Tabs for Module and Resources - now at the top */}
@@ -278,93 +289,97 @@ export default function ModulesPageContent({
               />
             </div>
             {/* Contextual button that changes based on active tab */}
-            {activeTab === "modules" ? (
-              <Suspense fallback={<Button disabled>Loading...</Button>}>
-                <ModuleOperations sessionId={sessionId} />
-              </Suspense>
-            ) : isSignedIn ? (
-              <ResourceUploadButton
-                variant="outline"
-                moduleId={preselectedModuleId || undefined}
-                initialOpen={openResourceUpload}
-              />
-            ) : null}
+            <div className="flex-shrink-0">
+              {activeTab === "modules" ? (
+                <Suspense fallback={<Button disabled>Loading...</Button>}>
+                  <ModuleOperations sessionId={sessionId} />
+                </Suspense>
+              ) : isSignedIn ? (
+                <ResourceUploadButton
+                  variant="outline"
+                  moduleId={preselectedModuleId || undefined}
+                  initialOpen={openResourceUpload}
+                />
+              ) : null}
+            </div>
           </div>
 
           <TabsContent value="modules" className="mt-2">
             {isLoading ? (
-              <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
-                {/* No skeleton cards as requested */}
+              <div className="flex items-center justify-center pt-10">
+                <Loader2 className="animate-spin" />
               </div>
             ) : filteredModules.length === 0 ? (
               <div className="flex flex-col items-center justify-center space-y-4 rounded-lg border border-dashed p-8 text-center">
-                <h3 className="text-xl font-medium">No modules found</h3>
-                <p className="text-muted-foreground">
+                <h3 className="text-lg font-medium">
+                  {searchQuery
+                    ? "No modules found"
+                    : "Modules are a way to organise your conversations and uploaded resources."}
+                </h3>
+                <p className="text-lg text-muted-foreground">
                   {searchQuery
-                    ? "Try a different search term"
-                    : "Create your first module to get started"}
+                    ? "Try a different search term" : "Create a module to get started."}
                 </p>
                 {!searchQuery}
               </div>
             ) : (
-              <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
-                {filteredModules.map((module) => {
-                  // Make sure module name exists and is not empty before encoding
-                  if (!module.name) {
-                    console.error("Module name is missing", module);
-                    return null;
-                  }
+              <div className="h-[calc(100vh-230px)] overflow-y-auto pb-6">
+                <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
+                  {filteredModules.map((module) => {
+                    // Make sure module name exists and is not empty before encoding
+                    if (!module.name) {
+                      console.error("Module name is missing", module);
+                      return null;
+                    }
 
-                  const moduleSlug = encodeModuleSlug(module.name);
-                  console.log(
-                    `Creating link for "${module.name}" → "${moduleSlug}"`
-                  );
-
-                  // Ensure moduleSlug is not empty
-                  if (!moduleSlug) {
-                    console.error(
-                      "Failed to encode module slug for",
-                      module.name
+                    const moduleSlug = encodeModuleSlug(module.name);
+                    console.log(
+                      `Creating link for "${module.name}" → "${moduleSlug}"`
                     );
-                    return null;
-                  }
 
-                  return (
-                    <Link
-                      href={`/modules/${moduleSlug}`}
-                      key={module.id || module.name}
-                      prefetch={true}
-                    >
-                      <Card className="hover:bg-muted/50 transition-colors">
-                        <CardHeader>
-                          <CardTitle>
-                            <span className="mr-2">{module.icon}</span>
-                            {module.name}
-                          </CardTitle>
-                          <CardDescription>
-                            {module.description || "No description"}
-                          </CardDescription>
-                        </CardHeader>
-                        <CardContent>
-                          <div className="text-sm text-muted-foreground">
-                            {module.resourceCount}{" "}
-                            {module.resourceCount === 1
-                              ? "resource"
-                              : "resources"}
-                          </div>
-                        </CardContent>
-                        <CardFooter className="text-xs text-muted-foreground">
-                          Updated {formatDate(module.updatedAt)}
-                        </CardFooter>
-                      </Card>
-                    </Link>
-                  );
-                })}
+                    // Ensure moduleSlug is not empty
+                    if (!moduleSlug) {
+                      console.error(
+                        "Failed to encode module slug for",
+                        module.name
+                      );
+                      return null;
+                    }
+
+                    return (
+                      <Link
+                        href={`/modules/${moduleSlug}`}
+                        key={module.id || module.name}
+                        prefetch={true}
+                      >
+                        <Card className="hover:bg-muted/50 transition-colors">
+                          <CardHeader>
+                            <CardTitle>
+                              <span className="mr-2">{module.icon}</span>
+                              {module.name}
+                            </CardTitle>
+                          </CardHeader>
+                          <CardContent>
+                            <div className="text-sm text-muted-foreground">
+                              {module.resourceCount}{" "}
+                              {module.resourceCount === 1
+                                ? "resource"
+                                : "resources"}
+                            </div>
+                          </CardContent>
+                          <CardFooter className="text-xs text-muted-foreground">
+                            Updated {formatDate(module.updatedAt)}
+                          </CardFooter>
+                        </Card>
+                      </Link>
+                    );
+                  })}
+                </div>
               </div>
             )}
           </TabsContent>
 
-          <TabsContent value="resources" className="mt-2">
+          <TabsContent value="resources">
             <ResourcesWrapper />
           </TabsContent>
         </Tabs>
diff --git a/components/Resource/resource-row.tsx b/components/Resource/resource-row.tsx
index b86874b..8681adf 100644
--- a/components/Resource/resource-row.tsx
+++ b/components/Resource/resource-row.tsx
@@ -30,7 +30,7 @@ import {
 import { toast } from "sonner";
 import axios from "axios";
 import { useResourceUrl } from "@/lib/hooks/useResourceUrl";
-import { DeleteConfirmationDialog } from "@/components/dialogs/DeleteConfirmationDialog";
+import { DeleteConfirmationDialog } from "@/components/dialogs/ResourceDeleteConfirmationDialog";
 import {
   getFileTypeFromExtension,
   getFileTypeFromMimeType,
diff --git a/components/Resource/resource-upload-button.tsx b/components/Resource/resource-upload-button.tsx
index 2c0f546..cbcf9ce 100644
--- a/components/Resource/resource-upload-button.tsx
+++ b/components/Resource/resource-upload-button.tsx
@@ -44,8 +44,8 @@ export function ResourceUploadButton({
       <Button onClick={handleButtonClick} {...props}>
         {children || (
           <>
-            <Upload className="mr-2 h-4 w-4" />
-            Upload
+            <Upload className="h-4 w-4 mr-0 sm:mr-2" />
+            <span className="hidden sm:inline">Upload</span>
           </>
         )}
       </Button>
diff --git a/components/Sidebar/ChatHistory.tsx b/components/Sidebar/ChatHistory.tsx
index cbfc4e5..3e265a9 100644
--- a/components/Sidebar/ChatHistory.tsx
+++ b/components/Sidebar/ChatHistory.tsx
@@ -237,6 +237,7 @@ export default function ChatHistory({
                           <AlertDialogFooter>
                             <AlertDialogCancel>Cancel</AlertDialogCancel>
                             <AlertDialogAction
+                              className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
                               onClick={() => {
                                 if (chatToDelete) {
                                   handleDeleteChat(chatToDelete);
diff --git a/components/Sidebar/ModuleList.tsx b/components/Sidebar/ModuleList.tsx
index a701c33..ad1e60a 100644
--- a/components/Sidebar/ModuleList.tsx
+++ b/components/Sidebar/ModuleList.tsx
@@ -12,7 +12,7 @@ import {
   DialogTrigger,
   DialogTitle,
 } from "@/components/ui/dialog";
-import { ModuleForm } from "@/components/Modules/module-form";
+import { ModuleForm } from "@/components/dialogs/ModuleForm";
 import { Plus } from "lucide-react";
 
 // Define the Module type here instead of importing from Sidebar
@@ -114,13 +114,19 @@ export default function ModuleList({
             variant={pathname?.startsWith("/modules") ? "secondary" : "ghost"}
             className="justify-start hover:bg-accent w-40 pl-2 pb-2 text-left"
             asChild
+            title="Open Dashboard"
           >
             <Link href="/modules">Modules & Resources</Link>
           </Button>
 
           <Dialog open={isCreating} onOpenChange={setIsCreating}>
             <DialogTrigger asChild>
-              <Button variant="ghost" size="icon" className="hover:bg-accent h-8 w-8 mr-2.5">
+              <Button
+                variant="ghost"
+                size="icon"
+                className="hover:bg-accent h-8 w-8 mr-2.5"
+                title="Create New Module"
+              >
                 <Plus className="h-4 w-4" />
               </Button>
             </DialogTrigger>
diff --git a/components/Sidebar/SidebarParts.tsx b/components/Sidebar/SidebarParts.tsx
index aa2f84f..0162363 100644
--- a/components/Sidebar/SidebarParts.tsx
+++ b/components/Sidebar/SidebarParts.tsx
@@ -126,6 +126,7 @@ export const SidebarTrigger = React.forwardRef<
         state === "collapsed" ? "mx-auto" : "",
         className
       )}
+      title={state === "expanded" ? "Collapse Sidebar" : "Expand Sidebar"}
       onClick={(event) => {
         onClick?.(event);
         toggleSidebar();
diff --git a/components/Sidebar/UserSection.tsx b/components/Sidebar/UserSection.tsx
index f6cdf7d..5892a91 100644
--- a/components/Sidebar/UserSection.tsx
+++ b/components/Sidebar/UserSection.tsx
@@ -55,7 +55,7 @@ export default function UserSection() {
             }}
             showName
           />
-          <Button variant="ghost" size="icon" asChild>
+          <Button variant="ghost" size="icon" asChild title="Settings">
             <Link href="/settings">
               <Settings className="h-4 w-4" />
             </Link>
diff --git a/components/dialogs/GlobalModuleCreationDialog.tsx b/components/dialogs/GlobalModuleCreationDialog.tsx
new file mode 100644
index 0000000..a5ec76f
--- /dev/null
+++ b/components/dialogs/GlobalModuleCreationDialog.tsx
@@ -0,0 +1,67 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
+import { ModuleForm } from "@/components/dialogs/ModuleForm";
+import { useKeyboardShortcuts } from "@/context/keyboard-shortcuts-context";
+
+// Custom event name for opening the module dialog - must match the one in command-k.tsx
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+
+/**
+ * GlobalModuleDialog component
+ *
+ * This component provides a global dialog for creating new modules
+ * that can be triggered via the Cmd/Ctrl + J keyboard shortcut
+ * or via the Command+K menu
+ */
+export function GlobalModuleDialog() {
+  const [isOpen, setIsOpen] = useState(false);
+  const { registerShortcut, unregisterShortcut } = useKeyboardShortcuts();
+
+  // Register keyboard shortcut
+  useEffect(() => {
+    const handleKeyboardShortcut = (e: KeyboardEvent) => {
+      e.preventDefault();
+      setIsOpen(true);
+    };
+
+    // Register the shortcut (Cmd/Ctrl + J)
+    registerShortcut("cmd+j", handleKeyboardShortcut);
+
+    // Cleanup on unmount
+    return () => {
+      unregisterShortcut("cmd+j");
+    };
+  }, [registerShortcut, unregisterShortcut]);
+
+  // Listen for custom event to open the dialog from command-k
+  useEffect(() => {
+    const handleOpenDialog = () => {
+      setIsOpen(true);
+    };
+
+    document.addEventListener(OPEN_MODULE_DIALOG_EVENT, handleOpenDialog);
+    return () => {
+      document.removeEventListener(OPEN_MODULE_DIALOG_EVENT, handleOpenDialog);
+    };
+  }, []);
+
+  const handleSuccess = () => {
+    setIsOpen(false);
+  };
+
+  return (
+    <Dialog open={isOpen} onOpenChange={setIsOpen}>
+      <DialogContent>
+        <DialogTitle className="text-xl font-bold ml-4">
+          Create New Module
+        </DialogTitle>
+        <ModuleForm
+          onSuccess={handleSuccess}
+          successEventName="module-create-success"
+        />
+      </DialogContent>
+    </Dialog>
+  );
+}
diff --git a/components/Modules/module-form.tsx b/components/dialogs/ModuleForm.tsx
similarity index 100%
rename from components/Modules/module-form.tsx
rename to components/dialogs/ModuleForm.tsx
diff --git a/components/dialogs/DeleteConfirmationDialog.tsx b/components/dialogs/ResourceDeleteConfirmationDialog.tsx
similarity index 100%
rename from components/dialogs/DeleteConfirmationDialog.tsx
rename to components/dialogs/ResourceDeleteConfirmationDialog.tsx
diff --git a/components/ui/command.tsx b/components/ui/command.tsx
new file mode 100644
index 0000000..a3361fb
--- /dev/null
+++ b/components/ui/command.tsx
@@ -0,0 +1,153 @@
+"use client"
+
+import * as React from "react"
+import { type DialogProps } from "@radix-ui/react-dialog"
+import { Command as CommandPrimitive } from "cmdk"
+import { Search } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Dialog, DialogContent } from "@/components/ui/dialog"
+
+const Command = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive
+    ref={ref}
+    className={cn(
+      "flex h-full w-full flex-col overflow-hidden rounded-md bg-input text-popover-foreground",
+      className
+    )}
+    {...props}
+  />
+))
+Command.displayName = CommandPrimitive.displayName
+
+const CommandDialog = ({ children, ...props }: DialogProps) => {
+  return (
+    <Dialog {...props}>
+      <DialogContent className="overflow-hidden p-0">
+        <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
+          {children}
+        </Command>
+      </DialogContent>
+    </Dialog>
+  )
+}
+
+const CommandInput = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Input>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
+>(({ className, ...props }, ref) => (
+  <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
+    <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
+    <CommandPrimitive.Input
+      ref={ref}
+      className={cn(
+        "flex h-10 w-full pl-2 mr-8 rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
+        className
+      )}
+      {...props}
+    />
+  </div>
+))
+
+CommandInput.displayName = CommandPrimitive.Input.displayName
+
+const CommandList = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.List>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive.List
+    ref={ref}
+    className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
+    {...props}
+  />
+))
+
+CommandList.displayName = CommandPrimitive.List.displayName
+
+const CommandEmpty = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Empty>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
+>((props, ref) => (
+  <CommandPrimitive.Empty
+    ref={ref}
+    className="py-6 text-center text-sm"
+    {...props}
+  />
+))
+
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName
+
+const CommandGroup = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Group>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive.Group
+    ref={ref}
+    className={cn(
+      "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
+      className
+    )}
+    {...props}
+  />
+))
+
+CommandGroup.displayName = CommandPrimitive.Group.displayName
+
+const CommandSeparator = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Separator>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive.Separator
+    ref={ref}
+    className={cn("-mx-1 h-px bg-border", className)}
+    {...props}
+  />
+))
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName
+
+const CommandItem = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Item>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive.Item
+    ref={ref}
+    className={cn(
+      "relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+      className
+    )}
+    {...props}
+  />
+))
+
+CommandItem.displayName = CommandPrimitive.Item.displayName
+
+const CommandShortcut = ({
+  className,
+  ...props
+}: React.HTMLAttributes<HTMLSpanElement>) => {
+  return (
+    <span
+      className={cn(
+        "ml-auto text-xs tracking-widest text-muted-foreground",
+        className
+      )}
+      {...props}
+    />
+  )
+}
+CommandShortcut.displayName = "CommandShortcut"
+
+export {
+  Command,
+  CommandDialog,
+  CommandInput,
+  CommandList,
+  CommandEmpty,
+  CommandGroup,
+  CommandItem,
+  CommandShortcut,
+  CommandSeparator,
+}
diff --git a/context/keyboard-shortcuts-context.tsx b/context/keyboard-shortcuts-context.tsx
new file mode 100644
index 0000000..59a35c7
--- /dev/null
+++ b/context/keyboard-shortcuts-context.tsx
@@ -0,0 +1,105 @@
+"use client";
+
+import * as React from "react";
+import { useRouter } from "next/navigation";
+
+// Custom event name for opening the resource upload dialog
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+/**
+ * Keyboard Shortcuts Context provides a centralized way to handle keyboard shortcuts
+ * across the application. It includes functionality for handling common actions like
+ * creating a new chat or module.
+ */
+
+// Define the context interface
+interface KeyboardShortcutsContextType {
+  registerShortcut: (key: string, handler: (e: KeyboardEvent) => void) => void;
+  unregisterShortcut: (key: string) => void;
+}
+
+// Create the context with default values
+const KeyboardShortcutsContext =
+  React.createContext<KeyboardShortcutsContextType>({
+    registerShortcut: () => {},
+    unregisterShortcut: () => {},
+  });
+
+// Custom hook to use keyboard shortcuts
+export function useKeyboardShortcuts() {
+  return React.useContext(KeyboardShortcutsContext);
+}
+
+export function KeyboardShortcutsProvider({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  const router = useRouter();
+  const shortcutHandlersRef = React.useRef<
+    Map<string, (e: KeyboardEvent) => void>
+  >(new Map());
+
+  // Register a shortcut
+  const registerShortcut = React.useCallback(
+    (key: string, handler: (e: KeyboardEvent) => void) => {
+      shortcutHandlersRef.current.set(key, handler);
+    },
+    []
+  );
+
+  // Unregister a shortcut
+  const unregisterShortcut = React.useCallback((key: string) => {
+    shortcutHandlersRef.current.delete(key);
+  }, []);
+
+  // Handle keyboard events
+  React.useEffect(() => {
+    if (typeof window === "undefined") return;
+
+    const handleKeyDown = (event: KeyboardEvent) => {
+      // Command/Ctrl + I: New Chat
+      if ((event.metaKey || event.ctrlKey) && event.key === "i") {
+        event.preventDefault();
+        router.push("/chat");
+      }
+
+      // Command/Ctrl + J: New Module is now handled by GlobalModuleDialog component
+      // We removed the navigation code here to avoid conflicts
+
+      // Command/Ctrl + U: Upload Resource
+      if ((event.metaKey || event.ctrlKey) && event.key === "u") {
+        event.preventDefault();
+        // Dispatch custom event to open resource upload dialog
+        const customEvent = new CustomEvent(OPEN_RESOURCE_UPLOAD_DIALOG_EVENT);
+        document.dispatchEvent(customEvent);
+      }
+
+      // Execute any registered custom handlers
+      const shortcutKey = `${event.metaKey || event.ctrlKey ? "cmd+" : ""}${
+        event.key
+      }`;
+      const handler = shortcutHandlersRef.current.get(shortcutKey);
+      if (handler) {
+        handler(event);
+      }
+    };
+
+    window.addEventListener("keydown", handleKeyDown);
+    return () => window.removeEventListener("keydown", handleKeyDown);
+  }, [router]);
+
+  const contextValue = React.useMemo(
+    () => ({
+      registerShortcut,
+      unregisterShortcut,
+    }),
+    [registerShortcut, unregisterShortcut]
+  );
+
+  return (
+    <KeyboardShortcutsContext.Provider value={contextValue}>
+      {children}
+    </KeyboardShortcutsContext.Provider>
+  );
+}
diff --git a/lib/utils.ts b/lib/utils.ts
index a0714f4..6a5259f 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -137,3 +137,14 @@ export function formatChatTitle(message: string): string {
   const title = message.substring(0, 50);
   return title + (message.length > 50 ? "..." : "");
 }
+
+/**
+ * Get the appropriate keyboard modifier key based on operating system
+ * @returns The correct modifier key symbol/text for the current platform
+ */
+export function getModifierKey(): string {
+  if (typeof navigator === "undefined") return "Ctrl/⌘"; // Default for SSR
+
+  const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
+  return isMac ? "⌘" : "Ctrl";
+}
diff --git a/package-lock.json b/package-lock.json
index 73a0747..11a4fd5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,10 +40,12 @@
         "@tanstack/react-query": "^5.68.0",
         "@types/uuid": "^10.0.0",
         "@vercel/analytics": "^1.5.0",
+        "@vercel/speed-insights": "^1.2.0",
         "ai": "^4.1.54",
         "axios": "^1.8.1",
         "class-variance-authority": "^0.7.1",
         "clsx": "^2.1.1",
+        "cmdk": "^1.1.1",
         "dotenv": "^16.4.7",
         "langchain": "^0.3.19",
         "lucide-react": "^0.477.0",
@@ -5179,6 +5181,41 @@
         }
       }
     },
+    "node_modules/@vercel/speed-insights": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@vercel/speed-insights/-/speed-insights-1.2.0.tgz",
+      "integrity": "sha512-y9GVzrUJ2xmgtQlzFP2KhVRoCglwfRQgjyfY607aU0hh0Un6d0OUyrJkjuAlsV18qR4zfoFPs/BiIj9YDS6Wzw==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "@sveltejs/kit": "^1 || ^2",
+        "next": ">= 13",
+        "react": "^18 || ^19 || ^19.0.0-rc",
+        "svelte": ">= 4",
+        "vue": "^3",
+        "vue-router": "^4"
+      },
+      "peerDependenciesMeta": {
+        "@sveltejs/kit": {
+          "optional": true
+        },
+        "next": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        },
+        "svelte": {
+          "optional": true
+        },
+        "vue": {
+          "optional": true
+        },
+        "vue-router": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/abort-controller": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -5994,6 +6031,22 @@
         "node": ">=6"
       }
     },
+    "node_modules/cmdk": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz",
+      "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-compose-refs": "^1.1.1",
+        "@radix-ui/react-dialog": "^1.1.6",
+        "@radix-ui/react-id": "^1.1.0",
+        "@radix-ui/react-primitive": "^2.0.2"
+      },
+      "peerDependencies": {
+        "react": "^18 || ^19 || ^19.0.0-rc",
+        "react-dom": "^18 || ^19 || ^19.0.0-rc"
+      }
+    },
     "node_modules/color": {
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
diff --git a/package.json b/package.json
index b5f09e0..363eac2 100644
--- a/package.json
+++ b/package.json
@@ -42,10 +42,12 @@
     "@tanstack/react-query": "^5.68.0",
     "@types/uuid": "^10.0.0",
     "@vercel/analytics": "^1.5.0",
+    "@vercel/speed-insights": "^1.2.0",
     "ai": "^4.1.54",
     "axios": "^1.8.1",
     "class-variance-authority": "^0.7.1",
     "clsx": "^2.1.1",
+    "cmdk": "^1.1.1",
     "dotenv": "^16.4.7",
     "langchain": "^0.3.19",
     "lucide-react": "^0.477.0",


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 9**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>components/Modules/module-detail-page-wrapper.tsx (1)</summary><blockquote>

`728-732`: _⚠️ Potential issue_

**Add keyboard event handlers for accessibility.**

The clickable elements for editing title and description need keyboard event handlers to be fully accessible.


To address the accessibility issues flagged by static analysis, add keyboard event handlers:

```diff
<h1
  className="text-xl font-bold cursor-pointer hover:bg-muted/50 px-2 py-1.5 rounded"
  onClick={() => setIsEditingTitle(true)}
+ onKeyDown={(e) => {
+   if (e.key === 'Enter' || e.key === ' ') {
+     e.preventDefault();
+     setIsEditingTitle(true);
+   }
+ }}
+ tabIndex={0}
+ role="button"
+ aria-label="Edit module title"
>
  {module.name}
</h1>
```

And similarly for the description:

```diff
<p
  className="text-muted-foreground cursor-pointer hover:bg-muted/50 p-4 rounded min-h-[158px]"
  onClick={() => setIsEditingDescription(true)}
+ onKeyDown={(e) => {
+   if (e.key === 'Enter' || e.key === ' ') {
+     e.preventDefault();
+     setIsEditingDescription(true);
+   }
+ }}
+ tabIndex={0}
+ role="button"
+ aria-label="Edit module description"
>
  {module.description || "Give context to your agent..."}
</p>
```


Also applies to: 799-804

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 728-732: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7a2b7fee0cd54c066aeeec75950ef1097336642f and 314b6b1752bbb1061e6e19e94f04921efc80e467.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`

</details>

<details>
<summary>📒 Files selected for processing (19)</summary>

* `app/layout.tsx` (3 hunks)
* `app/modules/[moduleName]/loading.tsx` (1 hunks)
* `app/modules/loading.tsx` (1 hunks)
* `app/settings/page.tsx` (1 hunks)
* `components/Main/command-k.tsx` (1 hunks)
* `components/Modules/module-actions.tsx` (3 hunks)
* `components/Modules/module-detail-page-wrapper.tsx` (5 hunks)
* `components/Modules/module-operations.tsx` (2 hunks)
* `components/Modules/modules-content.tsx` (5 hunks)
* `components/Resource/resource-row.tsx` (1 hunks)
* `components/Resource/resource-upload-button.tsx` (1 hunks)
* `components/Sidebar/ChatHistory.tsx` (1 hunks)
* `components/Sidebar/ModuleList.tsx` (2 hunks)
* `components/Sidebar/SidebarParts.tsx` (1 hunks)
* `components/Sidebar/UserSection.tsx` (1 hunks)
* `components/dialogs/GlobalModuleCreationDialog.tsx` (1 hunks)
* `components/ui/command.tsx` (1 hunks)
* `context/keyboard-shortcuts-context.tsx` (1 hunks)
* `package.json` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (4)</summary>

<details>
<summary>app/layout.tsx (3)</summary><blockquote>

<details>
<summary>context/keyboard-shortcuts-context.tsx (1)</summary>

* `KeyboardShortcutsProvider` (30-94)

</details>
<details>
<summary>components/dialogs/GlobalModuleCreationDialog.tsx (1)</summary>

* `GlobalModuleDialog` (18-67)

</details>
<details>
<summary>components/Main/command-k.tsx (1)</summary>

* `CommandK` (44-247)

</details>

</blockquote></details>
<details>
<summary>components/dialogs/GlobalModuleCreationDialog.tsx (3)</summary><blockquote>

<details>
<summary>context/keyboard-shortcuts-context.tsx (1)</summary>

* `useKeyboardShortcuts` (26-28)

</details>
<details>
<summary>components/ui/dialog.tsx (3)</summary>

* `Dialog` (112-112)
* `DialogContent` (117-117)
* `DialogTitle` (120-120)

</details>
<details>
<summary>components/dialogs/ModuleForm.tsx (1)</summary>

* `ModuleForm` (66-278)

</details>

</blockquote></details>
<details>
<summary>components/Main/command-k.tsx (4)</summary><blockquote>

<details>
<summary>lib/api.ts (1)</summary>

* `api` (7-223)

</details>
<details>
<summary>lib/utils.ts (1)</summary>

* `encodeModuleSlug` (41-77)

</details>
<details>
<summary>components/ui/command.tsx (8)</summary>

* `CommandDialog` (145-145)
* `CommandInput` (146-146)
* `CommandList` (147-147)
* `CommandEmpty` (148-148)
* `CommandGroup` (149-149)
* `CommandItem` (150-150)
* `CommandShortcut` (151-151)
* `CommandSeparator` (152-152)

</details>
<details>
<summary>components/ui/dialog.tsx (1)</summary>

* `DialogTitle` (120-120)

</details>

</blockquote></details>
<details>
<summary>components/Modules/modules-content.tsx (4)</summary><blockquote>

<details>
<summary>components/Resource/resource-table-skeleton.tsx (1)</summary>

* `ResourceTableSkeleton` (9-62)

</details>
<details>
<summary>components/Modules/module-operations.tsx (1)</summary>

* `ModuleOperations` (20-52)

</details>
<details>
<summary>components/Resource/resource-upload-button.tsx (1)</summary>

* `ResourceUploadButton` (14-68)

</details>
<details>
<summary>lib/utils.ts (2)</summary>

* `encodeModuleSlug` (41-77)
* `formatDate` (8-34)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>components/Modules/module-detail-page-wrapper.tsx</summary>

[error] 728-732: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)

---

[error] 799-802: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (42)</summary><blockquote>

<details>
<summary>components/Resource/resource-upload-button.tsx (1)</summary>

`47-48`: **Improved responsive design for the upload button**

The changes enhance the button's responsiveness by hiding the "Upload" text on mobile screens while maintaining the icon visibility across all screen sizes. This creates a more compact interface on smaller devices without sacrificing functionality.

</details>
<details>
<summary>components/Modules/module-operations.tsx (3)</summary>

`13-13`: **Import path updated for better organization**

The updated import path reflects a reorganization of components, moving the ModuleForm to a more logical location within the dialogs folder.

---

`35-38`: **Improved responsive design for the large module creation button**

The changes enhance the button's user experience by:
1. Preventing text wrapping with `whitespace-nowrap`
2. Making the UI responsive by hiding text on small screens
3. Maintaining the icon visibility across all screen sizes

This is consistent with the responsive design patterns used in other components.

---

`40-43`: **Improved responsive design for the standard module creation button**

Similar to the large button variant, these changes improve the responsive behavior on smaller screens while maintaining a consistent design approach throughout the application.

</details>
<details>
<summary>package.json (2)</summary>

`45-45`: **Added Vercel Speed Insights for performance monitoring**

The addition of `@vercel/speed-insights` will help monitor and analyze the application's performance, which is valuable for maintaining a good user experience.

---

`50-50`: **Added command palette library to support keyboard shortcuts**

The addition of the `cmdk` library supports the implementation of a command palette, which aligns with the PR's objective of adding keyboard shortcuts and enhancing user interaction.

</details>
<details>
<summary>components/Sidebar/SidebarParts.tsx (1)</summary>

`129-129`: **Accessibility improvement for sidebar toggle**

Adding a title attribute to the SidebarTrigger button provides a helpful tooltip that clearly indicates the action that will occur when clicked, improving accessibility and user experience.

</details>
<details>
<summary>app/modules/[moduleName]/loading.tsx (1)</summary>

`41-41`: **Good terminology update**

Changing the heading from "Description" to "Content" improves consistency with the rest of the application and better reflects the purpose of this section in the loading state.

</details>
<details>
<summary>app/settings/page.tsx (1)</summary>

`235-245`: **Excellent keyboard shortcut additions**

The addition of keyboard shortcuts for "New Chat" (⌘+I) and "New Module" (⌘+J) aligns perfectly with the PR objectives of enhancing application navigation and efficiency. These shortcuts will provide users with quicker access to common actions.

</details>
<details>
<summary>components/Sidebar/UserSection.tsx (1)</summary>

`58-58`: **Accessibility improvement for Settings button**

Adding a title attribute to the Settings button enhances accessibility by providing a tooltip that clarifies the button's purpose. This is consistent with the accessibility improvements made elsewhere in the codebase.

</details>
<details>
<summary>components/Sidebar/ChatHistory.tsx (1)</summary>

`240-240`: **Good enhancement of destructive action styling.**

Adding the destructive styling class to the delete button improves the user interface by clearly indicating that this action is destructive. The color scheme follows UI conventions for dangerous actions, providing better visual feedback to users.

</details>
<details>
<summary>components/Resource/resource-row.tsx (1)</summary>

`33-33`: **Good improvement to component import specificity.**

Updating the import path to use `ResourceDeleteConfirmationDialog` instead of a more generic name makes the code more maintainable and self-documenting. This change better reflects the component's specific purpose.

</details>
<details>
<summary>app/layout.tsx (3)</summary>

`8-8`: **Well-structured imports for new functionality.**

The added imports properly bring in the new components needed to implement keyboard shortcuts, command palette, and global module creation - directly supporting the PR objectives.



Also applies to: 13-13, 19-19, 22-22

---

`50-51`: **Documentation updated correctly.**

The provider hierarchy documentation was properly updated to reflect the new provider structure, maintaining code documentation accuracy.

---

`71-85`: **Well-implemented provider hierarchy for keyboard shortcuts.**

The implementation effectively:
1. Wraps the SidebarProvider with KeyboardShortcutsProvider, making shortcuts available across the application
2. Adds the CommandK and GlobalModuleDialog components in the right context
3. Includes SpeedInsights for performance monitoring

This structure provides a solid foundation for the keyboard shortcuts functionality while maintaining a clean component hierarchy.

</details>
<details>
<summary>components/Modules/module-detail-page-wrapper.tsx (3)</summary>

`658-667`: **Good addition of back navigation.**

Adding a back button improves the navigation experience, making it easier for users to return to the previous page. The button is appropriately styled and uses the correct icon for back navigation.

---

`728-728`: **Better spacing and alignment.**

The adjustments to padding (py-1.5) for the module title and adding margin (mr-8) to the content container improve the visual spacing and alignment of the UI elements.



Also applies to: 750-750

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 728-732: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)

</details>

</details>

---

`752-752`: **Improved content terminology and guidance.**

Changing "Description" to "Content" and updating the placeholder text to "Give context to your agent..." provides clearer guidance to users about the purpose of this field. This makes the application more intuitive to use.



Also applies to: 760-760, 802-802

</details>
<details>
<summary>components/Modules/module-actions.tsx (3)</summary>

`10-16`: **Import organization and ModuleForm path update**

The import statements have been nicely organized and the ModuleForm import path has been updated from Modules directory to dialogs directory. This change aligns with what appears to be a broader refactoring of dialog components to a dedicated location.

---

`119-119`: **Accessibility improvement with title attribute**

Adding the title attribute to the Edit Module button enhances accessibility for users relying on screen readers or tooltips.

---

`143-143`: **Accessibility improvement with title attribute**

Adding the title attribute to the Delete Module button enhances accessibility for users relying on screen readers or tooltips.

</details>
<details>
<summary>components/dialogs/GlobalModuleCreationDialog.tsx (5)</summary>

`1-7`: **Well-structured imports and client directive**

The imports are well-organized and the "use client" directive is correctly placed at the top of the file to indicate this is a client component.

---

`8-10`: **Good use of constants and documentation**

The constant OPEN_MODULE_DIALOG_EVENT is clearly named and well-documented with a comment explaining its purpose and relationship with the command-k component.

---

`11-18`: **Clear component documentation**

The component is well-documented with a JSDoc comment that clearly explains its purpose and functionality, including how it can be triggered.

---

`19-53`: **Well-implemented keyboard shortcut and event handling**

The keyboard shortcut implementation using the keyboard shortcuts context is clean and properly includes cleanup in the useEffect hooks. The component also correctly handles the custom event for opening the dialog.

---

`54-66`: <details>
<summary>✅ Verification successful</summary>

**Proper dialog implementation with clear UI hierarchy**

The dialog implementation is clean and follows best practices. The component structure is logical with a clear title and form. The success handler correctly closes the dialog upon successful form submission.


One minor point to verify - make sure the "module-create-success" event name in line 62 matches what the ModuleForm component expects.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if ModuleForm handles this event name appropriately
grep -r "module-create-success" --include="*.tsx" --include="*.ts" .
```

Length of output: 1157

---



</details>

**Dialog Implementation Verified: Code Approved**

The dialog structure and UI hierarchy are correctly implemented. The event name `"module-create-success"` is consistently used across components (e.g., in the Sidebar's module list), which confirms that the `ModuleForm` component receives the proper event name. No changes are required.

</details>
<details>
<summary>components/Modules/modules-content.tsx (10)</summary>

`6-6`: **Added Loader2 import for improved loading indicators**

Added Loader2 import to enhance the loading state visualization throughout the component.

---

`204-204`: **Simplified wrapper component markup**

Replaced a potentially more complex wrapper with a simpler div element, which is appropriate for this context.

---

`215-241`: **Improved conditional rendering with better user guidance**

The conditional rendering logic has been significantly improved:
1. Clearer separation between authenticated and unauthenticated states
2. Better loading state representation with ResourceTableSkeleton
3. More informative messaging based on search context

This provides a better user experience by offering appropriate guidance in different states.

---

`260-260`: **Improved layout with right margin**

Added margin-right to the main content container, which will create better visual spacing.

---

`262-262`: **Updated header text from "Categories" to "Dashboard"**

Changed the header label from "Categories" to "Dashboard", which better represents the purpose of this section as mentioned in the PR objectives.

---

`292-304`: **Improved contextual button rendering**

Enhanced the contextual button rendering with:
1. Better suspense handling for ModuleOperations
2. Conditional rendering of ResourceUploadButton based on authentication state
3. Clear separation of concerns for different tab contexts

This provides a more intuitive and responsive user interface.

---

`309-311`: **Enhanced loading state visualization**

Replaced the previous loading indicator with a centered spinning Loader2 component, which provides better visual feedback.

---

`314-322`: **Improved empty state messaging**

Updated the empty state messages to be more descriptive and context-sensitive:
1. More informative guidance when no modules exist
2. Clear indication of next steps based on whether a search was performed

This enhances user understanding and guides them toward appropriate actions.

---

`326-378`: **Enhanced module grid rendering with robust error handling**

The module grid rendering has been improved with:
1. Better error handling for edge cases (missing module names)
2. Detailed logging for debugging purposes
3. Proper slug encoding verification
4. Cleaner card layout for modules

This implementation is more resilient and provides better debugging information.

---

`382-382`: **Removed margins from resource tab content**

Simplified the TabsContent markup for resources by removing extra margin properties, which helps maintain consistent spacing.

</details>
<details>
<summary>components/Sidebar/ModuleList.tsx (3)</summary>

`15-15`: **Updated ModuleForm import path**

The import path for ModuleForm has been updated from `@/components/Modules/module-form` to `@/components/dialogs/ModuleForm`, aligning with the application-wide reorganization of dialog components.

---

`117-117`: **Added accessibility title to dashboard button**

Added a title attribute to the dashboard button, enhancing accessibility for screen reader users and providing helpful tooltips.

---

`124-129`: **Added accessibility title to module creation button**

Added a title attribute to the module creation button, improving accessibility and providing clear context about the button's purpose.


This button's functionality is now complemented by the new keyboard shortcut (Cmd/Ctrl + J) implemented in the GlobalModuleDialog component, providing alternative access methods for module creation.

</details>
<details>
<summary>context/keyboard-shortcuts-context.tsx (1)</summary>

`59-63`: <details>
<summary>❓ Verification inconclusive</summary>

**Confirm consistency of built-in shortcuts with the command palette.**

The default Command/Ctrl + I shortcut is handled here to navigate to “/chat,” but the `CommandK` component also offers a “Create New Chat” command with the same shortcut. Validate that no conflicting behaviors arise if both are triggered.

---

🏁 Script executed:

```shell
#!/bin/bash
# Verify potential duplication of Command/Ctrl+I usage in the codebase.
rg -A 3 "metaKey.*ctrlKey.*event.key.?==.?['\"]i['\"]"
```

Length of output: 60

---

I'll run an additional check to search for occurrences of the "CommandK" component, since the grep command targeting the shortcut didn't reveal any duplicates. This will help us verify if the "Create New Chat" binding in CommandK might conflict with the Command/Ctrl+I logic in the keyboard shortcuts context.



Once you review the output of this search, please verify that there isn’t a conflicting registration of the Command/Ctrl+I shortcut for the "Create New Chat" command in CommandK. 

---

**Action Required: Confirm Shortcut Consistency**

- Verify that Command/Ctrl+I (handled in `context/keyboard-shortcuts-context.tsx` at lines 59–63 to navigate to “/chat”) does not conflict with a similar binding registered in the `CommandK` component (which triggers “Create New Chat”).
- If both components are indeed binding the same shortcut, please adjust the key mappings to avoid unintended behavior.



---

🏁 Script executed:

```shell

#!/bin/bash
# Check for usage of the CommandK component in the codebase.
rg -A 5 -n "CommandK"
```

Length of output: 1381

---



</details>

**Confirm Shortcut Consistency: Manual Verification Needed**

- **Context:** In `context/keyboard-shortcuts-context.tsx` (lines 59–63) the shortcut Command/Ctrl+I is used to navigate to “/chat.”
- **Potential Issue:** The review comment noted that the `CommandK` component may use the same shortcut (for “Create New Chat”) and could conflict.
- **Next Steps:**
  - Verify that no key binding for Command/Ctrl+I is registered within `components/Main/command-k.tsx` that could overlap with the /chat navigation.
  - If the "Create New Chat" action is indeed bound to Command/Ctrl+I in the `CommandK` component, adjust the shortcut mappings to avoid any conflicting behavior.

Please confirm through manual inspection or additional tests that both components work in harmony without unintended shortcut conflicts.

</details>
<details>
<summary>components/Main/command-k.tsx (1)</summary>

`229-232`: **LGTM for chat module presentation.**

Rendering the associated module name below the chat title helps with quick identification. The consistent usage of `text-muted-foreground` for the subtitle is an excellent accessibility and styling choice.

</details>
<details>
<summary>components/ui/command.tsx (1)</summary>

`127-141`: **This approach effectively displays keyboard shortcuts.**

Using a dedicated `CommandShortcut` component encourages consistent styling and semantics. This is a good pattern for clearly conveying hotkeys to users.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T18:03:04Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>components/Main/command-k.tsx (2)</summary><blockquote>

`58-87`: **Provide user-friendly error feedback.**

When fetch fails, the code currently logs to console and resets data arrays. This repeats a prior suggestion to enhance error handling with a UI indication such as a toast.

---

`110-120`: **Potential redundancy with global shortcuts.**

Registering “cmd + k” with a local keydown listener may overlap with the `KeyboardShortcutsProvider`. Consider unifying this logic for consistency in how keyboard shortcuts are managed.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 314b6b1752bbb1061e6e19e94f04921efc80e467 and d2d955ed4c919b16c7c60c3ee570254ed6f94795.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `app/settings/page.tsx` (4 hunks)
* `components/Main/command-k.tsx` (1 hunks)
* `lib/utils.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>app/settings/page.tsx (1)</summary><blockquote>

<details>
<summary>lib/utils.ts (1)</summary>

* `getModifierKey` (145-150)

</details>

</blockquote></details>
<details>
<summary>components/Main/command-k.tsx (4)</summary><blockquote>

<details>
<summary>lib/utils.ts (2)</summary>

* `getModifierKey` (145-150)
* `encodeModuleSlug` (41-77)

</details>
<details>
<summary>lib/api.ts (1)</summary>

* `api` (7-223)

</details>
<details>
<summary>components/ui/command.tsx (8)</summary>

* `CommandDialog` (145-145)
* `CommandInput` (146-146)
* `CommandList` (147-147)
* `CommandEmpty` (148-148)
* `CommandGroup` (149-149)
* `CommandItem` (150-150)
* `CommandShortcut` (151-151)
* `CommandSeparator` (152-152)

</details>
<details>
<summary>components/ui/dialog.tsx (1)</summary>

* `DialogTitle` (120-120)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>lib/utils.ts (1)</summary>

`140-150`: **Function looks correct and user-friendly.**

This function appropriately checks the environment (SSG/SSR) and detects a macOS-like platform. It’s concise, handles edge cases by returning a fallback on SSR, and cleanly defaults to "Ctrl/⌘".

</details>
<details>
<summary>app/settings/page.tsx (3)</summary>

`3-3`: **Imports are suitable for client usage.**

Using the `useEffect` and `useState` hooks in this client component is correct. No concerns here.

---

`45-45`: **Importing `getModifierKey`.**

Importing the helper function in this file to dynamically display keyboard shortcuts is well-structured.

---

`236-266`: **Dynamic keyboard modifiers usage.**

Dynamically displaying the correct modifier key (⌘ or Ctrl) based on the user’s OS is a good user-experience improvement. The usage of `<kbd>` elements and consistent styling is on point.

</details>
<details>
<summary>components/Main/command-k.tsx (4)</summary>

`1-22`: **Client directive and event naming.**

Marking this component with `"use client"` ensures it runs in the browser. Defining `OPEN_MODULE_DIALOG_EVENT` is clear to communicate how the module dialog is triggered globally.

---

`45-56`: **State and OS modifier detection.**

Initializing local states for toggling the dialog and storing OS-specific modifiers is well-structured. The default fallback and immediate update inside `useEffect` ensures correct SSR behavior.

---

`122-129`: **New module dialog dispatch is succinct.**

Dispatching a custom event is straightforward and maintainable. Optionally, consider a success/failure user feedback mechanism if the module creation flow could fail.

---

`131-249`: **Good usage of Command Palette patterns.**

Creating new chats, filtering modules/chats, and navigating based on selection is cleanly implemented. The structure is easy to follow, and the code uses Next.js routing effectively.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T23:31:44Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 6**

<details>
<summary>♻️ Duplicate comments (5)</summary><blockquote>

<details>
<summary>app/settings/page.tsx (1)</summary><blockquote>

`3-3`: **Platform-aware keyboard shortcut detection.**

Good implementation of dynamic modifier key detection that respects the user's operating system. Storing `"Ctrl/⌘"` as a default for SSR and then updating it via `useEffect` aligns with best practices.




Also applies to: 45-45, 54-59

</blockquote></details>
<details>
<summary>components/Main/command-k.tsx (2)</summary><blockquote>

`61-89`: **Add user feedback for data fetch errors.**

Currently, the error handler only logs to console and sets modules/chats to empty arrays. Users won't know why no data appears when there's a fetch error.

Consider adding a toast notification or an error state in the UI to inform users when fetching fails.

---

`112-122`: **Consider using KeyboardShortcutsContext for Command+K handling.**

This direct keydown event listener could be redundant with the KeyboardShortcutsProvider approach.

For consistent shortcut management, consider registering "cmd+k" via the KeyboardShortcutsContext instead of a separate event listener.

</blockquote></details>
<details>
<summary>context/keyboard-shortcuts-context.tsx (2)</summary><blockquote>

`16-19`: **Use a stricter key format for improved maintainability.**

The context interface accepts a plain string for the "key", which could lead to inconsistencies.

Consider defining a more structured type for keys:

```diff
interface KeyboardShortcutsContextType {
-  registerShortcut: (key: string, handler: (e: KeyboardEvent) => void) => void;
-  unregisterShortcut: (key: string) => void;
+  registerShortcut: (key: KeyboardShortcutKey, handler: (e: KeyboardEvent) => void) => void;
+  unregisterShortcut: (key: KeyboardShortcutKey) => void;
}

+// Define a stricter format for keyboard shortcuts
+type KeyboardShortcutKey = `${'cmd' | 'ctrl' | 'shift' | 'alt'}+${string}` | string;
```

---

`44-48`: **Handle duplicate keyboard shortcut registrations.**

When registering the same key multiple times, the existing handler gets overwritten without warning.

Consider adding a warning or ensuring the behavior is documented:

```diff
const registerShortcut = React.useCallback(
  (key: string, handler: (e: KeyboardEvent) => void) => {
+    if (shortcutHandlersRef.current.has(key)) {
+      console.warn(`Keyboard shortcut '${key}' is being overwritten with a new handler.`);
+    }
    shortcutHandlersRef.current.set(key, handler);
  },
  []
);
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d2d955ed4c919b16c7c60c3ee570254ed6f94795 and 20d6ec54a4979ce78769d24fb64dcade00d73c55.

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `app/layout.tsx` (3 hunks)
* `app/settings/loading.tsx` (2 hunks)
* `app/settings/page.tsx` (4 hunks)
* `components/Main/GlobalResourceUploadDialog.tsx` (1 hunks)
* `components/Main/command-k.tsx` (1 hunks)
* `context/keyboard-shortcuts-context.tsx` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (4)</summary>

<details>
<summary>app/layout.tsx (3)</summary><blockquote>

<details>
<summary>context/keyboard-shortcuts-context.tsx (1)</summary>

* `KeyboardShortcutsProvider` (33-105)

</details>
<details>
<summary>components/dialogs/GlobalModuleCreationDialog.tsx (1)</summary>

* `GlobalModuleDialog` (18-67)

</details>
<details>
<summary>components/Main/command-k.tsx (1)</summary>

* `CommandK` (46-267)

</details>

</blockquote></details>
<details>
<summary>components/Main/GlobalResourceUploadDialog.tsx (1)</summary><blockquote>

<details>
<summary>components/dialogs/ResourceUploadDialog.tsx (1)</summary>

* `ResourceUploadDialog` (79-544)

</details>

</blockquote></details>
<details>
<summary>app/settings/page.tsx (1)</summary><blockquote>

<details>
<summary>lib/utils.ts (1)</summary>

* `getModifierKey` (145-150)

</details>

</blockquote></details>
<details>
<summary>components/Main/command-k.tsx (4)</summary><blockquote>

<details>
<summary>lib/utils.ts (2)</summary>

* `getModifierKey` (145-150)
* `encodeModuleSlug` (41-77)

</details>
<details>
<summary>lib/api.ts (1)</summary>

* `api` (7-223)

</details>
<details>
<summary>components/ui/command.tsx (8)</summary>

* `CommandDialog` (145-145)
* `CommandInput` (146-146)
* `CommandList` (147-147)
* `CommandEmpty` (148-148)
* `CommandGroup` (149-149)
* `CommandItem` (150-150)
* `CommandShortcut` (151-151)
* `CommandSeparator` (152-152)

</details>
<details>
<summary>components/ui/dialog.tsx (1)</summary>

* `DialogTitle` (120-120)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>app/settings/loading.tsx (2)</summary>

`45-45`: **Good adjustment to match the actual UI elements.**

The increase to five skeleton elements aligns with the five keyboard shortcuts now shown in the settings page, including the new "Upload Resource" shortcut.

---

`89-89`: **UI refinement for the email addresses section.**

Reducing to a single skeleton element for email addresses creates a more realistic loading state that better matches the actual content display.

</details>
<details>
<summary>components/Main/GlobalResourceUploadDialog.tsx (1)</summary>

`6-8`: **Good practice to document the event name origin.**

The comment clearly indicates where this event constant is defined, making it easier for other developers to track the relationship between components.

</details>
<details>
<summary>app/settings/page.tsx (2)</summary>

`236-238`: **Great UX improvement with platform-specific shortcut display.**

Replacing static "⌘" with the dynamic `modKey` variable improves the user experience by showing the correct modifier key based on the user's operating system. This change enhances accessibility and usability across different platforms.



Also applies to: 245-247, 254-256, 263-266, 272-274

---

`251-268`: **Good addition of the "Upload Resource" keyboard shortcut.**

The new keyboard shortcut for "Upload Resource" aligns with the PR objectives to enhance user interaction. The implementation is consistent with other shortcuts and properly uses the dynamic `modKey`.

</details>
<details>
<summary>app/layout.tsx (3)</summary>

`8-8`: **Well-organized imports for the new components.**

The imports are clearly organized and follow the project's import structure conventions, making it easy to understand the dependencies of this component.



Also applies to: 13-13, 19-20, 23-23

---

`51-52`: **Good documentation update.**

The provider structure documentation has been properly updated to reflect the new hierarchy, which helps other developers understand the component organization.

---

`72-88`: **Well-structured provider hierarchy with logical component placement.**

The modifications to the component structure are well-implemented:
1. `KeyboardShortcutsProvider` wraps the entire application UI to ensure shortcuts are globally available
2. Global dialogs and command palette are placed within the `SidebarProvider`
3. `SpeedInsights` is added for performance monitoring

This structure correctly supports the PR objectives of adding keyboard shortcuts and enhancing the module creation capabilities.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T23:58:04Z

Review comments:
diff_hunk: @@ -14,7 +14,7 @@ export default function Loading() {
   return (
     <div className="flex-1 space-y-4">
       <div className="flex items-center justify-between px-3 py-3.5">
-        <h1 className="font-bold text-xl">Categories</h1>
+        <h1 className="font-bold text-xl">Dashboard</h1>
path: app/modules/loading.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Header text updated from "Categories" to "Dashboard"**

The header text has been updated to "Dashboard" to align with other UI changes in the application. However, the JSDoc comment at line 10 still refers to "Categories" and should be updated for consistency.

```diff
- * It includes a header for "Categories", static tabs for "Modules" and "All Resources", a search bar skeleton,
+ * It includes a header for "Dashboard", static tabs for "Modules" and "All Resources", a search bar skeleton,
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
/**
- * It includes a header for "Categories", static tabs for "Modules" and "All Resources", a search bar skeleton,
+ * It includes a header for "Dashboard", static tabs for "Modules" and "All Resources", a search bar skeleton,
 */
        <h1 className="font-bold text-xl">Dashboard</h1>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T18:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+"use client";
+
+import * as React from "react";
+import { useRouter } from "next/navigation";
+
+/**
+ * Keyboard Shortcuts Context provides a centralized way to handle keyboard shortcuts
+ * across the application. It includes functionality for handling common actions like
+ * creating a new chat or module.
+ */
+
+// Define the context interface
+interface KeyboardShortcutsContextType {
+  registerShortcut: (key: string, handler: (e: KeyboardEvent) => void) => void;
+  unregisterShortcut: (key: string) => void;
+}
path: context/keyboard-shortcuts-context.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider defining stricter key formats for enhanced maintainability.**

Currently, the context interface accepts a plain string for the “key,” which might result in potential discrepancies (e.g., "cmd+i" vs "ctrl+i" vs just "i"). Narrowing this to a well-defined union type or adopting a specialized structure could prevent mistakes when registering or triggering shortcuts.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T18:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+"use client";
+
+import * as React from "react";
+import { useRouter } from "next/navigation";
+
+/**
+ * Keyboard Shortcuts Context provides a centralized way to handle keyboard shortcuts
+ * across the application. It includes functionality for handling common actions like
+ * creating a new chat or module.
+ */
+
+// Define the context interface
+interface KeyboardShortcutsContextType {
+  registerShortcut: (key: string, handler: (e: KeyboardEvent) => void) => void;
+  unregisterShortcut: (key: string) => void;
+}
+
+// Create the context with default values
+const KeyboardShortcutsContext =
+  React.createContext<KeyboardShortcutsContextType>({
+    registerShortcut: () => {},
+    unregisterShortcut: () => {},
+  });
+
+// Custom hook to use keyboard shortcuts
+export function useKeyboardShortcuts() {
+  return React.useContext(KeyboardShortcutsContext);
+}
+
+export function KeyboardShortcutsProvider({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  const router = useRouter();
+  const shortcutHandlersRef = React.useRef<
+    Map<string, (e: KeyboardEvent) => void>
+  >(new Map());
+
+  // Register a shortcut
+  const registerShortcut = React.useCallback(
+    (key: string, handler: (e: KeyboardEvent) => void) => {
+      shortcutHandlersRef.current.set(key, handler);
+    },
+    []
path: context/keyboard-shortcuts-context.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Ensure graceful handling for duplicate shortcut registrations.**

When registering the same key multiple times, the current implementation overwrites the existing handler in `Map`. If that’s desired, consider explicitly documenting it. Otherwise, add a check or warning to avoid accidental shortcut overrides.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T18:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,247 @@
+"use client";
+
+import * as React from "react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import {
+  CommandDialog,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+  CommandSeparator,
+  CommandShortcut,
+} from "@/components/ui/command";
+import { DialogTitle } from "@/components/ui/dialog";
+import { api } from "@/lib/api";
+import { encodeModuleSlug } from "@/lib/utils";
+
+// Custom event name for opening the module dialog
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+
+// Interface for module objects
+interface Module {
+  id: string;
+  name: string;
+  description?: string;
+  icon?: string;
+}
+
+// Interface for chat objects
+interface Chat {
+  id: string;
+  title: string;
+  moduleId?: string;
+  module?: {
+    id: string;
+    name: string;
+    icon?: string;
+  };
+  updatedAt?: string;
+}
+
+export function CommandK() {
+  const [open, setOpen] = useState(false);
+  const [modules, setModules] = useState<Module[]>([]);
+  const [chats, setChats] = useState<Chat[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [searchQuery, setSearchQuery] = useState("");
+  const router = useRouter();
+
+  // Fetch modules and chats when the command palette opens
+  useEffect(() => {
+    if (open) {
+      const fetchData = async () => {
+        try {
+          setLoading(true);
+
+          // Fetch modules and chats in parallel
+          const [modulesResponse, chatsResponse] = await Promise.all([
+            api.getModules(),
+            api.getChatHistory(),
+          ]);
+
+          setModules(modulesResponse.modules || []);
+          setChats(chatsResponse || []);
+        } catch (error) {
+          console.error("Error fetching data:", error);
+          setModules([]);
+          setChats([]);
+        } finally {
+          setLoading(false);
+        }
+      };
+
+      fetchData();
+    } else {
+      // Reset search when closing the dialog
+      setSearchQuery("");
+    }
+  }, [open]);
path: components/Main/command-k.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Enhance user feedback upon data fetch failure.**

Currently, an error only logs to console and sets modules/chats to empty arrays. Providing user-friendly feedback in the UI (e.g., a toast notification) would improve the user experience for scenarios where data fetching fails.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T18:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,247 @@
+"use client";
+
+import * as React from "react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import {
+  CommandDialog,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+  CommandSeparator,
+  CommandShortcut,
+} from "@/components/ui/command";
+import { DialogTitle } from "@/components/ui/dialog";
+import { api } from "@/lib/api";
+import { encodeModuleSlug } from "@/lib/utils";
+
+// Custom event name for opening the module dialog
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+
+// Interface for module objects
+interface Module {
+  id: string;
+  name: string;
+  description?: string;
+  icon?: string;
+}
+
+// Interface for chat objects
+interface Chat {
+  id: string;
+  title: string;
+  moduleId?: string;
+  module?: {
+    id: string;
+    name: string;
+    icon?: string;
+  };
+  updatedAt?: string;
+}
+
+export function CommandK() {
+  const [open, setOpen] = useState(false);
+  const [modules, setModules] = useState<Module[]>([]);
+  const [chats, setChats] = useState<Chat[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [searchQuery, setSearchQuery] = useState("");
+  const router = useRouter();
+
+  // Fetch modules and chats when the command palette opens
+  useEffect(() => {
+    if (open) {
+      const fetchData = async () => {
+        try {
+          setLoading(true);
+
+          // Fetch modules and chats in parallel
+          const [modulesResponse, chatsResponse] = await Promise.all([
+            api.getModules(),
+            api.getChatHistory(),
+          ]);
+
+          setModules(modulesResponse.modules || []);
+          setChats(chatsResponse || []);
+        } catch (error) {
+          console.error("Error fetching data:", error);
+          setModules([]);
+          setChats([]);
+        } finally {
+          setLoading(false);
+        }
+      };
+
+      fetchData();
+    } else {
+      // Reset search when closing the dialog
+      setSearchQuery("");
+    }
+  }, [open]);
+
+  // Filter modules and chats based on search query
+  const filteredResults = React.useMemo(() => {
+    if (!searchQuery.trim()) return { modules: [], chats: [] };
+
+    const normalizedQuery = searchQuery.toLowerCase().trim();
+
+    // Filter modules - search in name
+    const filteredModules = modules.filter((module) =>
+      module.name.toLowerCase().includes(normalizedQuery)
+    );
+
+    // Filter chats - search in both title and ID
+    const filteredChats = chats.filter(
+      (chat) =>
+        chat.title.toLowerCase().includes(normalizedQuery) ||
+        chat.id.toLowerCase().includes(normalizedQuery)
+    );
+
+    return { modules: filteredModules, chats: filteredChats };
+  }, [modules, chats, searchQuery]);
+
+  useEffect(() => {
+    const down = (e: KeyboardEvent) => {
+      if ((e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey)) {
+        e.preventDefault();
+        setOpen((open) => !open);
+      }
+    };
+
+    document.addEventListener("keydown", down);
+    return () => document.removeEventListener("keydown", down);
+  }, []);
path: components/Main/command-k.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Potential redundancy with KeyboardShortcutsProvider.**

The command palette toggles open on Command/Ctrl + K using a direct `keydown` listener here. If you prefer consistent management of keyboard shortcuts, consider reusing the `KeyboardShortcutsContext` approach by registering “cmd+k” to open/close this dialog instead of a separate event listener.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T18:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,247 @@
+"use client";
+
+import * as React from "react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import {
+  CommandDialog,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+  CommandSeparator,
+  CommandShortcut,
+} from "@/components/ui/command";
+import { DialogTitle } from "@/components/ui/dialog";
+import { api } from "@/lib/api";
+import { encodeModuleSlug } from "@/lib/utils";
+
+// Custom event name for opening the module dialog
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+
+// Interface for module objects
+interface Module {
+  id: string;
+  name: string;
+  description?: string;
+  icon?: string;
+}
+
+// Interface for chat objects
+interface Chat {
+  id: string;
+  title: string;
+  moduleId?: string;
+  module?: {
+    id: string;
+    name: string;
+    icon?: string;
+  };
+  updatedAt?: string;
+}
+
+export function CommandK() {
+  const [open, setOpen] = useState(false);
+  const [modules, setModules] = useState<Module[]>([]);
+  const [chats, setChats] = useState<Chat[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [searchQuery, setSearchQuery] = useState("");
+  const router = useRouter();
+
+  // Fetch modules and chats when the command palette opens
+  useEffect(() => {
+    if (open) {
+      const fetchData = async () => {
+        try {
+          setLoading(true);
+
+          // Fetch modules and chats in parallel
+          const [modulesResponse, chatsResponse] = await Promise.all([
+            api.getModules(),
+            api.getChatHistory(),
+          ]);
+
+          setModules(modulesResponse.modules || []);
+          setChats(chatsResponse || []);
+        } catch (error) {
+          console.error("Error fetching data:", error);
+          setModules([]);
+          setChats([]);
+        } finally {
+          setLoading(false);
+        }
+      };
+
+      fetchData();
+    } else {
+      // Reset search when closing the dialog
+      setSearchQuery("");
+    }
+  }, [open]);
+
+  // Filter modules and chats based on search query
+  const filteredResults = React.useMemo(() => {
+    if (!searchQuery.trim()) return { modules: [], chats: [] };
+
+    const normalizedQuery = searchQuery.toLowerCase().trim();
+
+    // Filter modules - search in name
+    const filteredModules = modules.filter((module) =>
+      module.name.toLowerCase().includes(normalizedQuery)
+    );
+
+    // Filter chats - search in both title and ID
+    const filteredChats = chats.filter(
+      (chat) =>
+        chat.title.toLowerCase().includes(normalizedQuery) ||
+        chat.id.toLowerCase().includes(normalizedQuery)
+    );
+
+    return { modules: filteredModules, chats: filteredChats };
+  }, [modules, chats, searchQuery]);
+
+  useEffect(() => {
+    const down = (e: KeyboardEvent) => {
+      if ((e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey)) {
+        e.preventDefault();
+        setOpen((open) => !open);
+      }
+    };
+
+    document.addEventListener("keydown", down);
+    return () => document.removeEventListener("keydown", down);
+  }, []);
+
+  // Handler for creating a new module
+  const handleCreateModule = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the module dialog
+    // This will be caught by the GlobalModuleDialog component
+    const event = new CustomEvent(OPEN_MODULE_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for creating a new chat
+  const handleCreateChat = () => {
+    setOpen(false);
+    router.push("/chat");
+  };
+
+  // Handler for navigating to a module's detail page
+  const handleModuleSelect = (moduleName: string) => {
+    setOpen(false);
+    const encodedName = encodeModuleSlug(moduleName);
+    router.push(`/modules/${encodedName}`);
+  };
+
+  // Handler for navigating to a chat
+  const handleChatSelect = (chatId: string) => {
+    setOpen(false);
+    router.push(`/chat/${chatId}`);
+  };
+
+  return (
+    <CommandDialog open={open} onOpenChange={setOpen}>
+      <DialogTitle className="sr-only">Command Menu</DialogTitle>
+      <CommandInput
+        placeholder="Type a command or search for modules and chats..."
+        value={searchQuery}
+        onValueChange={setSearchQuery}
+        className="bg-transparent border-none focus:ring-0"
+      />
+      <CommandList>
+        <CommandEmpty>
+          {loading ? "Loading..." : "No results found."}
+        </CommandEmpty>
+
+        {!searchQuery && (
+          <>
+            <CommandGroup heading="Suggestions">
+              <CommandItem onSelect={handleCreateChat} value="create-new-chat">
+                <span>Create New Chat</span>
+                <CommandShortcut>⌘I</CommandShortcut>
+              </CommandItem>
path: components/Main/command-k.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Validate cross-platform notation for keyboard shortcuts.**

“⌘I” is shown as the shortcut, but users on Windows/Linux will see Ctrl+I. Consider referencing a more generic label (like “Ctrl/⌘+I”) or performing run-time detection of the platform to display the correct modifier key.

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit d2d955e
created_at: 2025-04-08T18:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,153 @@
+"use client"
+
+import * as React from "react"
+import { type DialogProps } from "@radix-ui/react-dialog"
+import { Command as CommandPrimitive } from "cmdk"
+import { Search } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Dialog, DialogContent } from "@/components/ui/dialog"
+
+const Command = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive
+    ref={ref}
+    className={cn(
+      "flex h-full w-full flex-col overflow-hidden rounded-md bg-input text-popover-foreground",
+      className
+    )}
+    {...props}
+  />
+))
+Command.displayName = CommandPrimitive.displayName
+
+const CommandDialog = ({ children, ...props }: DialogProps) => {
+  return (
+    <Dialog {...props}>
+      <DialogContent className="overflow-hidden p-0">
+        <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
+          {children}
+        </Command>
+      </DialogContent>
+    </Dialog>
+  )
+}
path: components/ui/command.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider adding ARIA roles to improve dialog accessibility.**

The `CommandDialog` is composed with a `Dialog` wrapper. For enhanced screen reader support, ensure proper roles/attributes (e.g., role="dialog" with an accessible label).

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T18:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,153 @@
+"use client"
+
+import * as React from "react"
+import { type DialogProps } from "@radix-ui/react-dialog"
+import { Command as CommandPrimitive } from "cmdk"
+import { Search } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Dialog, DialogContent } from "@/components/ui/dialog"
+
+const Command = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive
+    ref={ref}
+    className={cn(
+      "flex h-full w-full flex-col overflow-hidden rounded-md bg-input text-popover-foreground",
+      className
+    )}
+    {...props}
+  />
+))
+Command.displayName = CommandPrimitive.displayName
+
+const CommandDialog = ({ children, ...props }: DialogProps) => {
+  return (
+    <Dialog {...props}>
+      <DialogContent className="overflow-hidden p-0">
+        <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
+          {children}
+        </Command>
+      </DialogContent>
+    </Dialog>
+  )
+}
+
+const CommandInput = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Input>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
+>(({ className, ...props }, ref) => (
+  <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
+    <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
+    <CommandPrimitive.Input
+      ref={ref}
+      className={cn(
+        "flex h-10 w-full pl-2 mr-8 rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
+        className
+      )}
+      {...props}
+    />
+  </div>
+))
+
+CommandInput.displayName = CommandPrimitive.Input.displayName
+
+const CommandList = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.List>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive.List
+    ref={ref}
+    className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
+    {...props}
+  />
+))
+
+CommandList.displayName = CommandPrimitive.List.displayName
+
+const CommandEmpty = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Empty>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
+>((props, ref) => (
+  <CommandPrimitive.Empty
+    ref={ref}
+    className="py-6 text-center text-sm"
+    {...props}
+  />
+))
+
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName
+
+const CommandGroup = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Group>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive.Group
+    ref={ref}
+    className={cn(
+      "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
+      className
+    )}
+    {...props}
+  />
+))
+
+CommandGroup.displayName = CommandPrimitive.Group.displayName
+
+const CommandSeparator = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Separator>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive.Separator
+    ref={ref}
+    className={cn("-mx-1 h-px bg-border", className)}
+    {...props}
+  />
+))
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName
+
+const CommandItem = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Item>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive.Item
+    ref={ref}
+    className={cn(
+      "relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+      className
+    )}
+    {...props}
+  />
path: components/ui/command.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Optionally add ARIA state for selected/disabled items.**

`cmdk` handles keyboard navigation and “selected” state via data attributes, but screen readers might not automatically interpret these. Consider adding `role="option"` or `aria-disabled` to reflect item states for improved accessibility.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T18:03:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,153 @@
+"use client"
+
+import * as React from "react"
+import { type DialogProps } from "@radix-ui/react-dialog"
+import { Command as CommandPrimitive } from "cmdk"
+import { Search } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Dialog, DialogContent } from "@/components/ui/dialog"
+
+const Command = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive>
+>(({ className, ...props }, ref) => (
+  <CommandPrimitive
+    ref={ref}
+    className={cn(
+      "flex h-full w-full flex-col overflow-hidden rounded-md bg-input text-popover-foreground",
+      className
+    )}
+    {...props}
+  />
+))
+Command.displayName = CommandPrimitive.displayName
+
+const CommandDialog = ({ children, ...props }: DialogProps) => {
+  return (
+    <Dialog {...props}>
+      <DialogContent className="overflow-hidden p-0">
+        <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
+          {children}
+        </Command>
+      </DialogContent>
+    </Dialog>
+  )
+}
+
+const CommandInput = React.forwardRef<
+  React.ElementRef<typeof CommandPrimitive.Input>,
+  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
+>(({ className, ...props }, ref) => (
+  <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
+    <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
+    <CommandPrimitive.Input
+      ref={ref}
+      className={cn(
+        "flex h-10 w-full pl-2 mr-8 rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
+        className
+      )}
+      {...props}
+    />
+  </div>
path: components/ui/command.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Add an explicit label for the search input.**

While the placeholder helps, screen readers benefit from an accessible label or `aria-label`. This can improve discoverability for users relying on assistive technology.

```diff
  <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
+   <label htmlFor="command-search" className="sr-only">
+     Search commands
+   </label>
    <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
    <CommandPrimitive.Input
+     id="command-search"
      ref={ref}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
    <label htmlFor="command-search" className="sr-only">
      Search commands
    </label>
    <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
    <CommandPrimitive.Input
      id="command-search"
      ref={ref}
      className={cn(
        "flex h-10 w-full pl-2 mr-8 rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
        className
      )}
      {...props}
    />
  </div>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T18:03:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,6 +51,12 @@ function SettingsContent() {
   const router = useRouter();
   const [isDeleteDialogOpen, setIsDeleteDialogOpen] = React.useState(false);
   const fileInputRef = useRef<HTMLInputElement>(null);
+  const [modKey, setModKey] = useState<string>("Ctrl/⌘"); // Default value for SSR
+
+  useEffect(() => {
path: app/settings/page.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Platform-aware keyboard shortcut detection.**

Storing `"Ctrl/⌘"` as a default for SSR and then updating it via `useEffect` aligns with best practices. Consider centralizing the logic in a context/utility if used widely.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T23:31:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+"use client";
+
+import * as React from "react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import {
+  CommandDialog,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+  CommandSeparator,
+  CommandShortcut,
+} from "@/components/ui/command";
+import { DialogTitle } from "@/components/ui/dialog";
+import { api } from "@/lib/api";
+import { encodeModuleSlug, getModifierKey } from "@/lib/utils";
+
+// Custom event name for opening the module dialog
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+
+// Interface for module objects
+interface Module {
+  id: string;
+  name: string;
+  description?: string;
+  icon?: string;
+}
+
+// Interface for chat objects
+interface Chat {
+  id: string;
+  title: string;
+  moduleId?: string;
+  module?: {
+    id: string;
+    name: string;
+    icon?: string;
+  };
+  updatedAt?: string;
+}
path: components/Main/command-k.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Interfaces for Module and Chat.**

The interface definitions are straightforward. If these are used in multiple places, consider extracting them into a shared types file for broader reuse.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T23:31:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,34 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { ResourceUploadDialog } from "@/components/dialogs/ResourceUploadDialog";
+
+// Event name that was defined in command-k.tsx
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+export function GlobalResourceUploadDialog() {
+  const [open, setOpen] = useState(false);
+
+  useEffect(() => {
+    // Function to handle the custom event
+    const handleOpenDialog = () => {
+      setOpen(true);
+    };
+
+    // Add event listener
+    document.addEventListener(
+      OPEN_RESOURCE_UPLOAD_DIALOG_EVENT,
+      handleOpenDialog
+    );
+
+    // Clean up event listener
+    return () => {
+      document.removeEventListener(
+        OPEN_RESOURCE_UPLOAD_DIALOG_EVENT,
+        handleOpenDialog
+      );
+    };
+  }, []);
+
+  return <ResourceUploadDialog open={open} onOpenChange={setOpen} />;
+}
path: components/Main/GlobalResourceUploadDialog.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Well-implemented dialog component with proper event handling.**

The component correctly uses React hooks for state management and properly cleans up event listeners when unmounting. This implementation follows the established pattern for global dialogs in the application.


However, consider supporting a `preselectedModuleId` prop like the underlying `ResourceUploadDialog` does:

```diff
-export function GlobalResourceUploadDialog() {
+export function GlobalResourceUploadDialog({ preselectedModuleId }: { preselectedModuleId?: string }) {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    // Function to handle the custom event
    const handleOpenDialog = () => {
      setOpen(true);
    };

    // Add event listener
    document.addEventListener(
      OPEN_RESOURCE_UPLOAD_DIALOG_EVENT,
      handleOpenDialog
    );

    // Clean up event listener
    return () => {
      document.removeEventListener(
        OPEN_RESOURCE_UPLOAD_DIALOG_EVENT,
        handleOpenDialog
      );
    };
  }, []);

-  return <ResourceUploadDialog open={open} onOpenChange={setOpen} />;
+  return <ResourceUploadDialog open={open} onOpenChange={setOpen} preselectedModuleId={preselectedModuleId} />;
}
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export function GlobalResourceUploadDialog({ preselectedModuleId }: { preselectedModuleId?: string }) {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    // Function to handle the custom event
    const handleOpenDialog = () => {
      setOpen(true);
    };

    // Add event listener
    document.addEventListener(
      OPEN_RESOURCE_UPLOAD_DIALOG_EVENT,
      handleOpenDialog
    );

    // Clean up event listener
    return () => {
      document.removeEventListener(
        OPEN_RESOURCE_UPLOAD_DIALOG_EVENT,
        handleOpenDialog
      );
    };
  }, []);

  return <ResourceUploadDialog open={open} onOpenChange={setOpen} preselectedModuleId={preselectedModuleId} />;
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T23:58:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,267 @@
+"use client";
+
+import * as React from "react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import {
+  CommandDialog,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+  CommandSeparator,
+  CommandShortcut,
+} from "@/components/ui/command";
+import { DialogTitle } from "@/components/ui/dialog";
+import { api } from "@/lib/api";
+import { encodeModuleSlug, getModifierKey } from "@/lib/utils";
+
+// Custom event name for opening the module dialog
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+// Custom event name for opening the resource upload dialog
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+// Interface for module objects
+interface Module {
+  id: string;
+  name: string;
+  description?: string;
+  icon?: string;
+}
+
+// Interface for chat objects
+interface Chat {
+  id: string;
+  title: string;
+  moduleId?: string;
+  module?: {
+    id: string;
+    name: string;
+    icon?: string;
+  };
+  updatedAt?: string;
+}
+
+export function CommandK() {
+  const [open, setOpen] = useState(false);
+  const [modules, setModules] = useState<Module[]>([]);
+  const [chats, setChats] = useState<Chat[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [searchQuery, setSearchQuery] = useState("");
+  const [modKey, setModKey] = useState("Ctrl/⌘"); // Default for SSR
+  const router = useRouter();
+
+  // Set the correct modifier key based on platform
+  useEffect(() => {
+    setModKey(getModifierKey());
+  }, []);
+
+  // Fetch modules and chats when the command palette opens
+  useEffect(() => {
+    if (open) {
+      const fetchData = async () => {
+        try {
+          setLoading(true);
+
+          // Fetch modules and chats in parallel
+          const [modulesResponse, chatsResponse] = await Promise.all([
+            api.getModules(),
+            api.getChatHistory(),
+          ]);
+
+          setModules(modulesResponse.modules || []);
+          setChats(chatsResponse || []);
+        } catch (error) {
+          console.error("Error fetching data:", error);
+          setModules([]);
+          setChats([]);
+        } finally {
+          setLoading(false);
+        }
+      };
+
+      fetchData();
+    } else {
+      // Reset search when closing the dialog
+      setSearchQuery("");
+    }
+  }, [open]);
+
+  // Filter modules and chats based on search query
+  const filteredResults = React.useMemo(() => {
+    if (!searchQuery.trim()) return { modules: [], chats: [] };
+
+    const normalizedQuery = searchQuery.toLowerCase().trim();
+
+    // Filter modules - search in name
+    const filteredModules = modules.filter((module) =>
+      module.name.toLowerCase().includes(normalizedQuery)
+    );
+
+    // Filter chats - search in both title and ID
+    const filteredChats = chats.filter(
+      (chat) =>
+        chat.title.toLowerCase().includes(normalizedQuery) ||
+        chat.id.toLowerCase().includes(normalizedQuery)
+    );
+
+    return { modules: filteredModules, chats: filteredChats };
+  }, [modules, chats, searchQuery]);
+
+  useEffect(() => {
+    const down = (e: KeyboardEvent) => {
+      if ((e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey)) {
+        e.preventDefault();
+        setOpen((open) => !open);
+      }
+    };
+
+    document.addEventListener("keydown", down);
+    return () => document.removeEventListener("keydown", down);
+  }, []);
+
+  // Handler for creating a new module
+  const handleCreateModule = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the module dialog
+    // This will be caught by the GlobalModuleDialog component
+    const event = new CustomEvent(OPEN_MODULE_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for opening the resource upload dialog
+  const handleResourceUpload = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the resource upload dialog
+    const event = new CustomEvent(OPEN_RESOURCE_UPLOAD_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for creating a new chat
+  const handleCreateChat = () => {
+    setOpen(false);
+    router.push("/chat");
+  };
+
+  // Handler for navigating to a module's detail page
+  const handleModuleSelect = (moduleName: string) => {
+    setOpen(false);
+    const encodedName = encodeModuleSlug(moduleName);
+    router.push(`/modules/${encodedName}`);
+  };
path: components/Main/command-k.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Error handling missing for module navigation.**

The `handleModuleSelect` function doesn't handle potential errors that might occur from the encoding process or during navigation.

Add a try/catch block to handle potential errors:

```diff
const handleModuleSelect = (moduleName: string) => {
  setOpen(false);
-  const encodedName = encodeModuleSlug(moduleName);
-  router.push(`/modules/${encodedName}`);
+  try {
+    const encodedName = encodeModuleSlug(moduleName);
+    router.push(`/modules/${encodedName}`);
+  } catch (error) {
+    console.error("Error navigating to module:", error);
+    // Provide user feedback about the navigation error
+  }
};
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  const handleModuleSelect = (moduleName: string) => {
    setOpen(false);
    try {
      const encodedName = encodeModuleSlug(moduleName);
      router.push(`/modules/${encodedName}`);
    } catch (error) {
      console.error("Error navigating to module:", error);
      // Provide user feedback about the navigation error
    }
  };
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T23:58:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,267 @@
+"use client";
+
+import * as React from "react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import {
+  CommandDialog,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+  CommandSeparator,
+  CommandShortcut,
+} from "@/components/ui/command";
+import { DialogTitle } from "@/components/ui/dialog";
+import { api } from "@/lib/api";
+import { encodeModuleSlug, getModifierKey } from "@/lib/utils";
+
+// Custom event name for opening the module dialog
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+// Custom event name for opening the resource upload dialog
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+// Interface for module objects
+interface Module {
+  id: string;
+  name: string;
+  description?: string;
+  icon?: string;
+}
+
+// Interface for chat objects
+interface Chat {
+  id: string;
+  title: string;
+  moduleId?: string;
+  module?: {
+    id: string;
+    name: string;
+    icon?: string;
+  };
+  updatedAt?: string;
+}
+
+export function CommandK() {
+  const [open, setOpen] = useState(false);
+  const [modules, setModules] = useState<Module[]>([]);
+  const [chats, setChats] = useState<Chat[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [searchQuery, setSearchQuery] = useState("");
+  const [modKey, setModKey] = useState("Ctrl/⌘"); // Default for SSR
+  const router = useRouter();
+
+  // Set the correct modifier key based on platform
+  useEffect(() => {
+    setModKey(getModifierKey());
+  }, []);
+
+  // Fetch modules and chats when the command palette opens
+  useEffect(() => {
+    if (open) {
+      const fetchData = async () => {
+        try {
+          setLoading(true);
+
+          // Fetch modules and chats in parallel
+          const [modulesResponse, chatsResponse] = await Promise.all([
+            api.getModules(),
+            api.getChatHistory(),
+          ]);
+
+          setModules(modulesResponse.modules || []);
+          setChats(chatsResponse || []);
+        } catch (error) {
+          console.error("Error fetching data:", error);
+          setModules([]);
+          setChats([]);
+        } finally {
+          setLoading(false);
+        }
+      };
+
+      fetchData();
+    } else {
+      // Reset search when closing the dialog
+      setSearchQuery("");
+    }
+  }, [open]);
+
+  // Filter modules and chats based on search query
+  const filteredResults = React.useMemo(() => {
+    if (!searchQuery.trim()) return { modules: [], chats: [] };
+
+    const normalizedQuery = searchQuery.toLowerCase().trim();
+
+    // Filter modules - search in name
+    const filteredModules = modules.filter((module) =>
+      module.name.toLowerCase().includes(normalizedQuery)
+    );
+
+    // Filter chats - search in both title and ID
+    const filteredChats = chats.filter(
+      (chat) =>
+        chat.title.toLowerCase().includes(normalizedQuery) ||
+        chat.id.toLowerCase().includes(normalizedQuery)
+    );
+
+    return { modules: filteredModules, chats: filteredChats };
+  }, [modules, chats, searchQuery]);
+
+  useEffect(() => {
+    const down = (e: KeyboardEvent) => {
+      if ((e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey)) {
+        e.preventDefault();
+        setOpen((open) => !open);
+      }
+    };
+
+    document.addEventListener("keydown", down);
+    return () => document.removeEventListener("keydown", down);
+  }, []);
+
+  // Handler for creating a new module
+  const handleCreateModule = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the module dialog
+    // This will be caught by the GlobalModuleDialog component
+    const event = new CustomEvent(OPEN_MODULE_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for opening the resource upload dialog
+  const handleResourceUpload = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the resource upload dialog
+    const event = new CustomEvent(OPEN_RESOURCE_UPLOAD_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for creating a new chat
+  const handleCreateChat = () => {
+    setOpen(false);
+    router.push("/chat");
+  };
+
+  // Handler for navigating to a module's detail page
+  const handleModuleSelect = (moduleName: string) => {
+    setOpen(false);
+    const encodedName = encodeModuleSlug(moduleName);
+    router.push(`/modules/${encodedName}`);
+  };
+
+  // Handler for navigating to a chat
+  const handleChatSelect = (chatId: string) => {
+    setOpen(false);
+    router.push(`/chat/${chatId}`);
+  };
+
+  return (
+    <CommandDialog open={open} onOpenChange={setOpen}>
+      <DialogTitle className="sr-only">Command Menu</DialogTitle>
+      <CommandInput
+        placeholder={`Type a command or search for modules and chats... (${modKey}+K)`}
+        value={searchQuery}
+        onValueChange={setSearchQuery}
+        className="bg-transparent border-none focus:ring-0"
+      />
+      <CommandList>
+        <CommandEmpty>
+          {loading ? "Loading..." : "No results found."}
+        </CommandEmpty>
+
+        {!searchQuery && (
+          <>
+            <CommandGroup heading="Suggestions">
+              <CommandItem onSelect={handleCreateChat} value="create-new-chat">
+                <span>Create New Chat</span>
+                <CommandShortcut>{modKey}I</CommandShortcut>
+              </CommandItem>
+              <CommandItem
+                onSelect={handleCreateModule}
+                value="create-new-module"
+              >
+                <span>Create New Module</span>
+                <CommandShortcut>{modKey}J</CommandShortcut>
+              </CommandItem>
+
+              <CommandItem
+                onSelect={handleResourceUpload}
+                value="upload-resource"
+              >
+                <span>Upload Resource</span>
+                <CommandShortcut>{modKey}U</CommandShortcut>
+              </CommandItem>
+            </CommandGroup>
+
+            <CommandSeparator />
+
+            <CommandGroup heading="Navigation">
+              <CommandItem
+                value="nav-modules"
+                onSelect={() => {
+                  setOpen(false);
+                  router.push("/modules");
+                }}
+              >
+                <span>Modules & Resources dashboard</span>
+              </CommandItem>
+              <CommandItem
+                value="nav-settings"
+                onSelect={() => {
+                  setOpen(false);
+                  router.push("/settings");
+                }}
+              >
+                <span>Settings</span>
+              </CommandItem>
+            </CommandGroup>
+          </>
+        )}
+
+        {filteredResults.modules.length > 0 && (
+          <CommandGroup heading="Modules">
+            {filteredResults.modules.map((module) => (
+              <CommandItem
+                key={module.id}
+                value={`module-${module.id}-${module.name.toLowerCase()}`}
+                onSelect={() => handleModuleSelect(module.name)}
+              >
+                <span>{module.name}</span>
+              </CommandItem>
+            ))}
+          </CommandGroup>
+        )}
+
+        {filteredResults.chats.length > 0 && (
+          <CommandGroup heading="Chats">
+            {filteredResults.chats.map((chat) => (
+              <CommandItem
+                key={chat.id}
+                value={`chat-${chat.id}-${chat.title.toLowerCase()}`}
+                onSelect={() => handleChatSelect(chat.id)}
+              >
+                <div className="flex justify-between w-full items-center">
+                  <div className="flex flex-col">
+                    <span>{chat.title}</span>
+                    {chat.module && (
+                      <span className="text-xs text-muted-foreground">
+                        {chat.module.name}
+                      </span>
+                    )}
+                  </div>
+                  {chat.updatedAt && (
+                    <span className="text-xs text-muted-foreground">
+                      {new Date(chat.updatedAt).toLocaleDateString()}
+                    </span>
+                  )}
path: components/Main/command-k.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Improve date formatting for better readability.**

The current date format uses the browser's default format which may not be the most readable.

Consider using a more user-friendly date format:

```diff
{chat.updatedAt && (
  <span className="text-xs text-muted-foreground">
-    {new Date(chat.updatedAt).toLocaleDateString()}
+    {new Intl.DateTimeFormat('en-US', { 
+      month: 'short', 
+      day: 'numeric',
+      year: new Date(chat.updatedAt).getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined 
+    }).format(new Date(chat.updatedAt))}
  </span>
)}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
                  {chat.updatedAt && (
                    <span className="text-xs text-muted-foreground">
                      {new Intl.DateTimeFormat('en-US', { 
                        month: 'short', 
                        day: 'numeric',
                        year: new Date(chat.updatedAt).getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined 
                      }).format(new Date(chat.updatedAt))}
                    </span>
                  )}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T23:58:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,267 @@
+"use client";
+
+import * as React from "react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import {
+  CommandDialog,
+  CommandEmpty,
+  CommandGroup,
+  CommandInput,
+  CommandItem,
+  CommandList,
+  CommandSeparator,
+  CommandShortcut,
+} from "@/components/ui/command";
+import { DialogTitle } from "@/components/ui/dialog";
+import { api } from "@/lib/api";
+import { encodeModuleSlug, getModifierKey } from "@/lib/utils";
+
+// Custom event name for opening the module dialog
+const OPEN_MODULE_DIALOG_EVENT = "open-module-dialog";
+// Custom event name for opening the resource upload dialog
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+// Interface for module objects
+interface Module {
+  id: string;
+  name: string;
+  description?: string;
+  icon?: string;
+}
+
+// Interface for chat objects
+interface Chat {
+  id: string;
+  title: string;
+  moduleId?: string;
+  module?: {
+    id: string;
+    name: string;
+    icon?: string;
+  };
+  updatedAt?: string;
+}
+
+export function CommandK() {
+  const [open, setOpen] = useState(false);
+  const [modules, setModules] = useState<Module[]>([]);
+  const [chats, setChats] = useState<Chat[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [searchQuery, setSearchQuery] = useState("");
+  const [modKey, setModKey] = useState("Ctrl/⌘"); // Default for SSR
+  const router = useRouter();
+
+  // Set the correct modifier key based on platform
+  useEffect(() => {
+    setModKey(getModifierKey());
+  }, []);
+
+  // Fetch modules and chats when the command palette opens
+  useEffect(() => {
+    if (open) {
+      const fetchData = async () => {
+        try {
+          setLoading(true);
+
+          // Fetch modules and chats in parallel
+          const [modulesResponse, chatsResponse] = await Promise.all([
+            api.getModules(),
+            api.getChatHistory(),
+          ]);
+
+          setModules(modulesResponse.modules || []);
+          setChats(chatsResponse || []);
+        } catch (error) {
+          console.error("Error fetching data:", error);
+          setModules([]);
+          setChats([]);
+        } finally {
+          setLoading(false);
+        }
+      };
+
+      fetchData();
+    } else {
+      // Reset search when closing the dialog
+      setSearchQuery("");
+    }
+  }, [open]);
+
+  // Filter modules and chats based on search query
+  const filteredResults = React.useMemo(() => {
+    if (!searchQuery.trim()) return { modules: [], chats: [] };
+
+    const normalizedQuery = searchQuery.toLowerCase().trim();
+
+    // Filter modules - search in name
+    const filteredModules = modules.filter((module) =>
+      module.name.toLowerCase().includes(normalizedQuery)
+    );
+
+    // Filter chats - search in both title and ID
+    const filteredChats = chats.filter(
+      (chat) =>
+        chat.title.toLowerCase().includes(normalizedQuery) ||
+        chat.id.toLowerCase().includes(normalizedQuery)
+    );
+
+    return { modules: filteredModules, chats: filteredChats };
+  }, [modules, chats, searchQuery]);
+
+  useEffect(() => {
+    const down = (e: KeyboardEvent) => {
+      if ((e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey)) {
+        e.preventDefault();
+        setOpen((open) => !open);
+      }
+    };
+
+    document.addEventListener("keydown", down);
+    return () => document.removeEventListener("keydown", down);
+  }, []);
+
+  // Handler for creating a new module
+  const handleCreateModule = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the module dialog
+    // This will be caught by the GlobalModuleDialog component
+    const event = new CustomEvent(OPEN_MODULE_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for opening the resource upload dialog
+  const handleResourceUpload = () => {
+    setOpen(false);
+    // Dispatch a custom event to open the resource upload dialog
+    const event = new CustomEvent(OPEN_RESOURCE_UPLOAD_DIALOG_EVENT);
+    document.dispatchEvent(event);
+  };
+
+  // Handler for creating a new chat
+  const handleCreateChat = () => {
+    setOpen(false);
+    router.push("/chat");
+  };
+
+  // Handler for navigating to a module's detail page
+  const handleModuleSelect = (moduleName: string) => {
+    setOpen(false);
+    const encodedName = encodeModuleSlug(moduleName);
+    router.push(`/modules/${encodedName}`);
+  };
+
+  // Handler for navigating to a chat
+  const handleChatSelect = (chatId: string) => {
+    setOpen(false);
+    router.push(`/chat/${chatId}`);
+  };
+
+  return (
+    <CommandDialog open={open} onOpenChange={setOpen}>
+      <DialogTitle className="sr-only">Command Menu</DialogTitle>
+      <CommandInput
+        placeholder={`Type a command or search for modules and chats... (${modKey}+K)`}
+        value={searchQuery}
+        onValueChange={setSearchQuery}
+        className="bg-transparent border-none focus:ring-0"
+      />
+      <CommandList>
+        <CommandEmpty>
+          {loading ? "Loading..." : "No results found."}
+        </CommandEmpty>
path: components/Main/command-k.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Enhance loading and empty states with better UX.**

The current loading and empty states are minimal text labels.

Consider enhancing these states with more informative messages or visual elements:

```diff
<CommandEmpty>
-  {loading ? "Loading..." : "No results found."}
+  {loading ? (
+    <div className="flex items-center justify-center py-6">
+      <span className="mr-2">Loading results</span>
+      {/* You could add a spinner component here */}
+    </div>
+  ) : (
+    <div className="text-center py-6">
+      <p>No results found for "{searchQuery}"</p>
+      <p className="text-sm text-muted-foreground mt-1">Try a different search term</p>
+    </div>
+  )}
</CommandEmpty>
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        <CommandEmpty>
          {loading ? (
            <div className="flex items-center justify-center py-6">
              <span className="mr-2">Loading results</span>
              {/* You could add a spinner component here */}
            </div>
          ) : (
            <div className="text-center py-6">
              <p>No results found for "{searchQuery}"</p>
              <p className="text-sm text-muted-foreground mt-1">Try a different search term</p>
            </div>
          )}
        </CommandEmpty>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T23:58:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,105 @@
+"use client";
+
+import * as React from "react";
+import { useRouter } from "next/navigation";
+
+// Custom event name for opening the resource upload dialog
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+/**
+ * Keyboard Shortcuts Context provides a centralized way to handle keyboard shortcuts
+ * across the application. It includes functionality for handling common actions like
+ * creating a new chat or module.
+ */
+
+// Define the context interface
+interface KeyboardShortcutsContextType {
+  registerShortcut: (key: string, handler: (e: KeyboardEvent) => void) => void;
+  unregisterShortcut: (key: string) => void;
+}
+
+// Create the context with default values
+const KeyboardShortcutsContext =
+  React.createContext<KeyboardShortcutsContextType>({
+    registerShortcut: () => {},
+    unregisterShortcut: () => {},
+  });
+
+// Custom hook to use keyboard shortcuts
+export function useKeyboardShortcuts() {
+  return React.useContext(KeyboardShortcutsContext);
+}
+
+export function KeyboardShortcutsProvider({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  const router = useRouter();
+  const shortcutHandlersRef = React.useRef<
+    Map<string, (e: KeyboardEvent) => void>
+  >(new Map());
+
+  // Register a shortcut
+  const registerShortcut = React.useCallback(
+    (key: string, handler: (e: KeyboardEvent) => void) => {
+      shortcutHandlersRef.current.set(key, handler);
+    },
+    []
+  );
+
+  // Unregister a shortcut
+  const unregisterShortcut = React.useCallback((key: string) => {
+    shortcutHandlersRef.current.delete(key);
+  }, []);
+
+  // Handle keyboard events
+  React.useEffect(() => {
+    if (typeof window === "undefined") return;
+
+    const handleKeyDown = (event: KeyboardEvent) => {
+      // Command/Ctrl + I: New Chat
+      if ((event.metaKey || event.ctrlKey) && event.key === "i") {
+        event.preventDefault();
+        router.push("/chat");
+      }
+
+      // Command/Ctrl + J: New Module is now handled by GlobalModuleDialog component
+      // We removed the navigation code here to avoid conflicts
+
+      // Command/Ctrl + U: Upload Resource
+      if ((event.metaKey || event.ctrlKey) && event.key === "u") {
+        event.preventDefault();
+        // Dispatch custom event to open resource upload dialog
+        const customEvent = new CustomEvent(OPEN_RESOURCE_UPLOAD_DIALOG_EVENT);
+        document.dispatchEvent(customEvent);
+      }
path: context/keyboard-shortcuts-context.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Check for active form elements before handling keyboard shortcuts.**

The current implementation captures all keyboard events, which may interfere with text input fields.

Add a check to avoid capturing shortcuts when the user is typing in form elements:

```diff
const handleKeyDown = (event: KeyboardEvent) => {
+  // Skip shortcut handling if the user is typing in an input field, textarea, etc.
+  const target = event.target as HTMLElement;
+  const isEditable = 
+    target.isContentEditable || 
+    ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName);
+  if (isEditable) return;

  // Command/Ctrl + I: New Chat
  if ((event.metaKey || event.ctrlKey) && event.key === "i") {
    event.preventDefault();
    router.push("/chat");
  }
  
  // Rest of the code...
};
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    const handleKeyDown = (event: KeyboardEvent) => {
+      // Skip shortcut handling if the user is typing in an input field, textarea, etc.
+      const target = event.target as HTMLElement;
+      const isEditable =
+        target.isContentEditable ||
+        ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName);
+      if (isEditable) return;
+
      // Command/Ctrl + I: New Chat
      if ((event.metaKey || event.ctrlKey) && event.key === "i") {
        event.preventDefault();
        router.push("/chat");
      }
      
      // Command/Ctrl + J: New Module is now handled by GlobalModuleDialog component
      // We removed the navigation code here to avoid conflicts
      
      // Command/Ctrl + U: Upload Resource
      if ((event.metaKey || event.ctrlKey) && event.key === "u") {
        event.preventDefault();
        // Dispatch custom event to open resource upload dialog
        const customEvent = new CustomEvent(OPEN_RESOURCE_UPLOAD_DIALOG_EVENT);
        document.dispatchEvent(customEvent);
      }
    };
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T23:58:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,105 @@
+"use client";
+
+import * as React from "react";
+import { useRouter } from "next/navigation";
+
+// Custom event name for opening the resource upload dialog
+const OPEN_RESOURCE_UPLOAD_DIALOG_EVENT = "open-resource-upload-dialog";
+
+/**
+ * Keyboard Shortcuts Context provides a centralized way to handle keyboard shortcuts
+ * across the application. It includes functionality for handling common actions like
+ * creating a new chat or module.
+ */
+
+// Define the context interface
+interface KeyboardShortcutsContextType {
+  registerShortcut: (key: string, handler: (e: KeyboardEvent) => void) => void;
+  unregisterShortcut: (key: string) => void;
+}
+
+// Create the context with default values
+const KeyboardShortcutsContext =
+  React.createContext<KeyboardShortcutsContextType>({
+    registerShortcut: () => {},
+    unregisterShortcut: () => {},
+  });
+
+// Custom hook to use keyboard shortcuts
+export function useKeyboardShortcuts() {
+  return React.useContext(KeyboardShortcutsContext);
+}
+
+export function KeyboardShortcutsProvider({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  const router = useRouter();
+  const shortcutHandlersRef = React.useRef<
+    Map<string, (e: KeyboardEvent) => void>
+  >(new Map());
+
+  // Register a shortcut
+  const registerShortcut = React.useCallback(
+    (key: string, handler: (e: KeyboardEvent) => void) => {
+      shortcutHandlersRef.current.set(key, handler);
+    },
+    []
+  );
+
+  // Unregister a shortcut
+  const unregisterShortcut = React.useCallback((key: string) => {
+    shortcutHandlersRef.current.delete(key);
+  }, []);
+
+  // Handle keyboard events
+  React.useEffect(() => {
+    if (typeof window === "undefined") return;
+
+    const handleKeyDown = (event: KeyboardEvent) => {
+      // Command/Ctrl + I: New Chat
+      if ((event.metaKey || event.ctrlKey) && event.key === "i") {
+        event.preventDefault();
+        router.push("/chat");
+      }
+
+      // Command/Ctrl + J: New Module is now handled by GlobalModuleDialog component
+      // We removed the navigation code here to avoid conflicts
+
+      // Command/Ctrl + U: Upload Resource
+      if ((event.metaKey || event.ctrlKey) && event.key === "u") {
+        event.preventDefault();
+        // Dispatch custom event to open resource upload dialog
+        const customEvent = new CustomEvent(OPEN_RESOURCE_UPLOAD_DIALOG_EVENT);
+        document.dispatchEvent(customEvent);
+      }
+
+      // Execute any registered custom handlers
+      const shortcutKey = `${event.metaKey || event.ctrlKey ? "cmd+" : ""}${
+        event.key
+      }`;
+      const handler = shortcutHandlersRef.current.get(shortcutKey);
+      if (handler) {
+        handler(event);
+      }
path: context/keyboard-shortcuts-context.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Improve shortcut key mapping for multi-modifier combinations.**

The current implementation doesn't properly handle combinations of modifier keys (e.g., Shift+Ctrl+A).

Enhance the modifier key detection:

```diff
// Execute any registered custom handlers
-const shortcutKey = `${event.metaKey || event.ctrlKey ? "cmd+" : ""}${
-  event.key
-}`;
+const modifiers = [
+  event.ctrlKey ? 'ctrl' : '',
+  event.metaKey ? 'cmd' : '',
+  event.shiftKey ? 'shift' : '',
+  event.altKey ? 'alt' : ''
+].filter(Boolean).join('+');
+const shortcutKey = modifiers ? `${modifiers}+${event.key}` : event.key;

const handler = shortcutHandlersRef.current.get(shortcutKey);
if (handler) {
  handler(event);
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      // Execute any registered custom handlers
-      const shortcutKey = `${event.metaKey || event.ctrlKey ? "cmd+" : ""}${
-        event.key
-      }`;
+      const modifiers = [
+        event.ctrlKey ? 'ctrl' : '',
+        event.metaKey ? 'cmd' : '',
+        event.shiftKey ? 'shift' : '',
+        event.altKey ? 'alt' : ''
+      ].filter(Boolean).join('+');
+      const shortcutKey = modifiers ? `${modifiers}+${event.key}` : event.key;
      
      const handler = shortcutHandlersRef.current.get(shortcutKey);
      if (handler) {
        handler(event);
      }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-08T23:58:04Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-04-08T17:58:21Z
body: [vc]: #6Cd8XZRHYfYdRyzzJ+nmvrlAYOPuaWUzgyRSpgvZX3k=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJzdHVkeS1jaGF0LWFwcCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJzdHVkeS1jaGF0LWFwcC1naXQtc2lkZWJhci1lZGl0cy1zaGFyYW4xMzAzcy1wcm9qZWN0cy52ZXJjZWwuYXBwIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zaGFyYW4xMzAzcy1wcm9qZWN0cy9zdHVkeS1jaGF0LWFwcC9CcWR0SDVmQmhvbkROcjk1eWJxSE1maFBQMXF5IiwicHJldmlld1VybCI6InN0dWR5LWNoYXQtYXBwLWdpdC1zaWRlYmFyLWVkaXRzLXNoYXJhbjEzMDNzLXByb2plY3RzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJyb290RGlyZWN0b3J5IjpudWxsfV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **study-chat-app** | ✅ Ready ([Inspect](https://vercel.com/sharan1303s-projects/study-chat-app/BqdtH5fBhonDNr95ybqHMfhPP1qy)) | [Visit Preview](https://study-chat-app-git-sidebar-edits-sharan1303s-projects.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/study-chat-app-git-sidebar-edits-sharan1303s-projects.vercel.app?via=pr-comment-feedback-link) | Apr 8, 2025 11:55pm |


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-04-08T17:58:25Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
The changes introduce a new provider for handling keyboard shortcuts and several new UI components. The layout is modified by wrapping the sidebar with a keyboard shortcuts provider and by adding a command palette component along with a global resource upload dialog. Several UI elements across loading screens, module pages, and sidebar components have updated texts, improved accessibility attributes, and adjusted styling. Import paths have been updated consistently, and new external dependencies have been added to support the added functionality.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `app/layout.tsx`, `app/settings/page.tsx`, `context/keyboard-shortcuts-context.tsx`, `components/Main/command-k.tsx`, `components/ui/command.tsx`, `components/dialogs/GlobalModuleCreationDialog.tsx`, `components/Main/GlobalResourceUploadDialog.tsx` | Introduce new keyboard shortcut support: added `KeyboardShortcutsProvider` wrapping the sidebar, new command palette (`CommandK` and UI components), a global module creation dialog, and new shortcuts in settings. |
| `app/modules/[moduleName]/loading.tsx`, `app/modules/loading.tsx`, `components/Modules/module-detail-page-wrapper.tsx`, `components/Modules/modules-content.tsx` | Update header texts and layout: change "Description" to "Content" and "Categories" to "Dashboard", adjust spacing and placeholder text for clarity. |
| `components/Modules/module-actions.tsx`, `components/Modules/module-operations.tsx`, `components/Sidebar/ModuleList.tsx`, `components/Resource/resource-row.tsx`, `components/Resource/resource-upload-button.tsx`, `components/Sidebar/SidebarParts.tsx`, `components/Sidebar/UserSection.tsx`, `components/Sidebar/ChatHistory.tsx` | Update import paths (e.g., `ModuleForm`, `DeleteConfirmationDialog`) and add `title` attributes plus refined styling for buttons and icons to improve accessibility and responsive layout. |
| `package.json` | Add new dependencies: `@vercel/speed-insights` (^1.2.0) and `cmdk` (^1.1.1). |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant U as User
    participant K as KeyboardShortcutsProvider
    participant CK as CommandK
    participant API as API Library
    participant R as Router

    U->>K: Press shortcut (Cmd/Ctrl+K)
    K->>CK: Open command palette
    CK->>API: Fetch modules & chats (parallel)
    API-->>CK: Return data
    U->>CK: Enter search query
    CK->>CK: Filter results
    U->>CK: Select an item
    CK->>R: Navigate to selected page
```

```mermaid
sequenceDiagram
    participant U as User
    participant K as KeyboardShortcutsProvider
    participant G as GlobalModuleDialog
    participant MF as ModuleForm
    participant API as API/Server

    U->>K: Press shortcut (Cmd/Ctrl+J) or trigger event
    K->>G: Open GlobalModuleDialog
    G->>U: Display ModuleForm
    U->>G: Submit module creation form
    G->>API: Send module creation request
    API-->>G: Confirm module creation
    G->>U: Close dialog and update view
```

## Poem
> Hop along the lines of code,  
> Where shortcuts ease our busy load.  
> A palette of commands in sight,  
> New dialogs dancing in the light.  
> I, the rabbit, cheer with glee,  
> For changes set the interface free! 🐰✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=sharan1303/study-chat-app&utm_content=38):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: notion-workspace[bot]
created_at: 2025-04-09T00:45:42Z
body: <a href="https://www.notion.so/cmd-k-menu-for-modules-files-and-chat-threads-1c37a719d3278052a511eb2e29eb2070?pvs=8&n=github_linkback">cmd k menu for modules, files and chat threads</a>
author_association: NONE