@@ -74,7 +74,7 @@ export type PromptSubmission = {
7474 selectedSkills ?: SkillInfo [ ] ;
7575 permissions ?: UserToolPermission [ ] ;
7676 alwaysAllows ?: PermissionScope [ ] ;
77- command ?: "new" | "resume" | "continue" | "undo" | "mcp" | "exit" ;
77+ command ?: "new" | "resume" | "continue" | "undo" | "mcp" | "keybind" | " exit";
7878} ;
7979
8080export type PromptDraft = {
@@ -293,7 +293,7 @@ export const PromptInput = React.memo(function PromptInput({
293293 if ( ! statusMessage ) {
294294 return ;
295295 }
296- const timer = setTimeout ( ( ) => setStatusMessage ( null ) , 2500 ) ;
296+ const timer = setTimeout ( ( ) => setStatusMessage ( null ) , 8000 ) ;
297297 return ( ) => clearTimeout ( timer ) ;
298298 } , [ statusMessage ] ) ;
299299
@@ -738,7 +738,18 @@ export const PromptInput = React.memo(function PromptInput({
738738 return ;
739739 }
740740 if ( item . kind === "keybind" ) {
741- handleKeybindCommand ( ) ;
741+ const parts = buffer . text . trim ( ) . split ( / \s + / ) ;
742+ if ( parts . length > 1 ) {
743+ // Has args: process add/remove/list subcommand
744+ handleKeybindCommand ( ) ;
745+ } else if ( parts [ 0 ] && parts [ 0 ] !== "/keybind" ) {
746+ // Partial match from menu (e.g. /key): auto-complete to /keybind
747+ setBuffer ( { text : "/keybind " , cursor : "/keybind " . length } ) ;
748+ clearUndoRedoStacks ( ) ;
749+ } else {
750+ // Exact match with no args: show usage hint
751+ setStatusMessage ( "Usage: /keybind add <shortcut> <action> | remove <shortcut> | list" ) ;
752+ }
742753 return ;
743754 }
744755 if ( item . kind === "exit" ) {
@@ -750,20 +761,19 @@ export const PromptInput = React.memo(function PromptInput({
750761 }
751762
752763 function handleKeybindCommand ( ) : void {
753- const parts = buffer . text . trim ( ) . split ( / \s + / ) ;
764+ const rawParts = buffer . text . trim ( ) . split ( / \s + / ) ;
765+ const isGlobal = rawParts . includes ( "--global" ) ;
766+ const parts = rawParts . filter ( ( p ) => p !== "--global" ) ;
754767 const subcommand = parts [ 1 ] ?? "list" ;
755768 const existingProjectSettings = readProjectSettings ( projectRoot ) ;
769+ const useProject = existingProjectSettings !== null ;
770+ const userSettings = readSettings ( ) ;
771+ const kbs = keybinds ?? { } ;
772+ const levelLabel = isGlobal ? " (global)" : useProject ? " (project)" : " (global)" ;
756773
757774 if ( subcommand === "list" ) {
758- clearSlashToken ( ) ;
759- const kbs = keybinds ?? { } ;
760- const entries = Object . entries ( kbs ) ;
761- if ( entries . length === 0 ) {
762- setStatusMessage ( "No custom keybinds configured. Use /keybind add <shortcut> <action>" ) ;
763- } else {
764- const lines = entries . map ( ( [ shortcut , action ] ) => ` ${ shortcut } → /${ action } ` ) ;
765- setStatusMessage ( `Keybinds:\n${ lines . join ( "\n" ) } ` ) ;
766- }
775+ onSubmit ( { text : "/keybind list" , imageUrls : [ ] , command : "keybind" } ) ;
776+ resetPromptInput ( ) ;
767777 return ;
768778 }
769779
@@ -786,52 +796,76 @@ export const PromptInput = React.memo(function PromptInput({
786796 // Validate action is a known slash command or skill
787797 const knownNames = new Set ( slashItems . map ( ( s ) => s . name ) ) ;
788798 if ( ! knownNames . has ( action ) ) {
789- setStatusMessage ( `Unknown action "/${ action } ". Use a slash command or skill name.` ) ;
799+ const available = slashItems
800+ . filter ( ( s ) => s . kind !== "skill" )
801+ . map ( ( s ) => s . name )
802+ . slice ( 0 , 10 )
803+ . join ( ", " ) ;
804+ setStatusMessage ( `Unknown action "/${ action } ". Available: ${ available } …` ) ;
790805 clearSlashToken ( ) ;
791806 return ;
792807 }
793808
794- const useProject = existingProjectSettings !== null ;
795- const rawSettings = useProject ? existingProjectSettings : readSettings ( ) ;
809+ // Check duplicates across all levels (merged)
810+ const existingMerged = kbs [ shortcut ] ;
811+ if ( existingMerged === action ) {
812+ setStatusMessage ( `Keybind already set: ${ shortcut } → /${ action } ` ) ;
813+ clearSlashToken ( ) ;
814+ return ;
815+ }
816+
817+ // Determine write target
818+ const rawSettings = isGlobal ? userSettings : useProject ? existingProjectSettings : userSettings ;
796819 const current : Record < string , string > = { ...( rawSettings ?. keybinds ?? { } ) } ;
797820 current [ shortcut ] = action ;
798821 const updated = { ...( rawSettings ?? { } ) , keybinds : current } ;
799- if ( useProject ) {
800- writeProjectSettings ( updated , projectRoot ) ;
801- } else {
822+ if ( isGlobal || ! useProject ) {
802823 writeSettings ( updated ) ;
824+ } else {
825+ writeProjectSettings ( updated , projectRoot ) ;
803826 }
804827 onKeybindsChanged ?.( ) ;
805- setStatusMessage ( `Keybind added: ${ shortcut } → /${ action } ` ) ;
828+ if ( existingMerged ) {
829+ setStatusMessage ( `Keybind updated: ${ shortcut } → /${ action } (was /${ existingMerged } )${ levelLabel } ` ) ;
830+ } else {
831+ setStatusMessage ( `Keybind added: ${ shortcut } → /${ action } ${ levelLabel } ` ) ;
832+ }
806833 clearSlashToken ( ) ;
807834 return ;
808835 }
809836
810837 if ( subcommand === "remove" ) {
811838 const shortcut = parts [ 2 ] ;
839+ const rawSettings = isGlobal ? userSettings : useProject ? existingProjectSettings : userSettings ;
840+ const current : Record < string , string > = { ...( rawSettings ?. keybinds ?? { } ) } ;
841+ const existingShortcuts = Object . keys ( kbs ) ;
812842 if ( ! shortcut ) {
813- setStatusMessage ( "Usage: /keybind remove <shortcut>" ) ;
843+ if ( existingShortcuts . length === 0 ) {
844+ setStatusMessage ( "No custom keybinds to remove." ) ;
845+ } else {
846+ const list = existingShortcuts . map ( ( s ) => `${ s } → /${ kbs [ s ] } ` ) . join ( ", " ) ;
847+ setStatusMessage ( `Usage: /keybind remove <shortcut>\nExisting: ${ list } ` ) ;
848+ }
814849 clearSlashToken ( ) ;
815850 return ;
816851 }
817852
818- const useProject = existingProjectSettings !== null ;
819- const rawSettings = useProject ? existingProjectSettings : readSettings ( ) ;
820- const current : Record < string , string > = { ...( rawSettings ?. keybinds ?? { } ) } ;
821853 if ( ! ( shortcut in current ) ) {
822- setStatusMessage ( `Keybind "${ shortcut } " not found.` ) ;
854+ const list =
855+ existingShortcuts . length > 0 ? existingShortcuts . map ( ( s ) => `${ s } → /${ kbs [ s ] } ` ) . join ( ", " ) : "(none)" ;
856+ setStatusMessage ( `Keybind "${ shortcut } " not found${ levelLabel } .\nExisting: ${ list } ` ) ;
823857 clearSlashToken ( ) ;
824858 return ;
825859 }
826860 delete current [ shortcut ] ;
827861 const updated = { ...( rawSettings ?? { } ) , keybinds : current } ;
828- if ( useProject ) {
829- writeProjectSettings ( updated , projectRoot ) ;
830- } else {
862+ if ( isGlobal || ! useProject ) {
831863 writeSettings ( updated ) ;
864+ } else {
865+ writeProjectSettings ( updated , projectRoot ) ;
832866 }
833867 onKeybindsChanged ?.( ) ;
834- setStatusMessage ( `Keybind removed: ${ shortcut } ` ) ;
868+ setStatusMessage ( `Keybind removed: ${ shortcut } ${ levelLabel } ` ) ;
835869 clearSlashToken ( ) ;
836870 return ;
837871 }
0 commit comments