33 ArrowRightOnRectangleIcon ,
44 BeakerIcon ,
55 BellAlertIcon ,
6- BookOpenIcon ,
76 ChartBarIcon ,
87 ChevronRightIcon ,
98 ClockIcon ,
@@ -21,6 +20,7 @@ import {
2120import { useNavigation } from "@remix-run/react" ;
2221import { useEffect , useRef , useState , type ReactNode } from "react" ;
2322import simplur from "simplur" ;
23+ import { AISparkleIcon } from "~/assets/icons/AISparkleIcon" ;
2424import { ConnectedIcon , DisconnectedIcon } from "~/assets/icons/ConnectionIcons" ;
2525import { RunsIcon } from "~/assets/icons/RunsIcon" ;
2626import { TaskIcon } from "~/assets/icons/TaskIcon" ;
@@ -59,16 +59,11 @@ import {
5959import connectedImage from "../../assets/images/cli-connected.png" ;
6060import disconnectedImage from "../../assets/images/cli-disconnected.png" ;
6161import { FreePlanUsage } from "../billing/FreePlanUsage" ;
62+ import { InlineCode } from "../code/InlineCode" ;
6263import { useDevPresence } from "../DevPresence" ;
6364import { ImpersonationBanner } from "../ImpersonationBanner" ;
6465import { Button , ButtonContent , LinkButton } from "../primitives/Buttons" ;
65- import {
66- Dialog ,
67- DialogContent ,
68- DialogFooter ,
69- DialogHeader ,
70- DialogTrigger ,
71- } from "../primitives/Dialog" ;
66+ import { Dialog , DialogContent , DialogHeader , DialogTrigger } from "../primitives/Dialog" ;
7267import { Paragraph } from "../primitives/Paragraph" ;
7368import {
7469 Popover ,
@@ -86,7 +81,8 @@ import { HelpAndFeedback } from "./HelpAndFeedbackPopover";
8681import { SideMenuHeader } from "./SideMenuHeader" ;
8782import { SideMenuItem } from "./SideMenuItem" ;
8883import { SideMenuSection } from "./SideMenuSection" ;
89- import { InlineCode } from "../code/InlineCode" ;
84+ import { useShortcutKeys } from "~/hooks/useShortcutKeys" ;
85+ import { ShortcutKey } from "../primitives/ShortcutKey" ;
9086
9187type SideMenuUser = Pick < User , "email" | "admin" > & { isImpersonating : boolean } ;
9288export type SideMenuProject = Pick <
@@ -116,6 +112,17 @@ export function SideMenu({
116112 const [ showHeaderDivider , setShowHeaderDivider ] = useState ( false ) ;
117113 const currentPlan = useCurrentPlan ( ) ;
118114 const isFreeUser = currentPlan ?. v3Subscription ?. isPaying === false ;
115+ const buttonRef = useRef < HTMLButtonElement > ( null ) ;
116+
117+ useShortcutKeys ( {
118+ shortcut : { key : "a" , modifiers : [ "mod" , "shift" ] } ,
119+ action : ( e ) => {
120+ e . preventDefault ( ) ;
121+ if ( buttonRef . current ) {
122+ buttonRef . current . click ( ) ;
123+ }
124+ } ,
125+ } ) ;
119126
120127 useEffect ( ( ) => {
121128 const handleScroll = ( ) => {
@@ -251,7 +258,34 @@ export function SideMenu({
251258 </ div >
252259 </ div >
253260 < div className = "flex flex-col gap-1 border-t border-grid-bright p-1" >
254- < HelpAndFeedback />
261+ < div className = "flex w-full items-center justify-between" >
262+ < HelpAndFeedback />
263+ < TooltipProvider disableHoverableContent >
264+ < Tooltip >
265+ < TooltipTrigger asChild >
266+ < div >
267+ < Button
268+ ref = { buttonRef }
269+ variant = "small-menu-item"
270+ data-action = "ask-ai"
271+ onClick = { ( ) => {
272+ // TODO: sort action
273+ } }
274+ >
275+ < AISparkleIcon className = "size-5" />
276+ </ Button >
277+ </ div >
278+ </ TooltipTrigger >
279+ < TooltipContent
280+ side = "top"
281+ className = "flex items-center gap-1 py-1.5 pl-2.5 pr-2 text-xs"
282+ >
283+ Ask AI
284+ < ShortcutKey shortcut = { { key : "/" , modifiers : [ "mod" ] } } variant = "small" />
285+ </ TooltipContent >
286+ </ Tooltip >
287+ </ TooltipProvider >
288+ </ div >
255289 { isFreeUser && (
256290 < FreePlanUsage
257291 to = { v3BillingPath ( organization ) }
0 commit comments