@@ -36,19 +36,24 @@ export function formattedBadge(type: string) {
3636 return null ;
3737}
3838
39- // random Script
40- function getRandomScript ( categories : Category [ ] ) : Script | null {
39+ function getRandomScript ( categories : Category [ ] , previouslySelected : Set < string > = new Set ( ) ) : Script | null {
4140 const allScripts = categories . flatMap ( cat => cat . scripts || [ ] ) ;
4241 if ( allScripts . length === 0 )
4342 return null ;
44- const idx = Math . floor ( Math . random ( ) * allScripts . length ) ;
45- return allScripts [ idx ] ;
43+
44+ const availableScripts = allScripts . filter ( script => ! previouslySelected . has ( script . slug ) ) ;
45+ if ( availableScripts . length === 0 ) {
46+ return allScripts [ Math . floor ( Math . random ( ) * allScripts . length ) ] ;
47+ }
48+ const idx = Math . floor ( Math . random ( ) * availableScripts . length ) ;
49+ return availableScripts [ idx ] ;
4650}
4751
48- export default function CommandMenu ( ) {
52+ function CommandMenu ( ) {
4953 const [ open , setOpen ] = React . useState ( false ) ;
5054 const [ links , setLinks ] = React . useState < Category [ ] > ( [ ] ) ;
5155 const [ isLoading , setIsLoading ] = React . useState ( false ) ;
56+ const [ selectedScripts , setSelectedScripts ] = React . useState < Set < string > > ( new Set ( ) ) ;
5257 const router = useRouter ( ) ;
5358
5459 const fetchSortedCategories = ( ) => {
@@ -65,25 +70,26 @@ export default function CommandMenu() {
6570 } ;
6671
6772 React . useEffect ( ( ) => {
68- const down = ( e : KeyboardEvent ) => {
73+ const handleKeyDown = ( e : KeyboardEvent ) => {
6974 if ( e . key === "k" && ( e . metaKey || e . ctrlKey ) ) {
7075 e . preventDefault ( ) ;
7176 fetchSortedCategories ( ) ;
7277 setOpen ( open => ! open ) ;
7378 }
7479 } ;
75- document . addEventListener ( "keydown" , down ) ;
76- return ( ) => document . removeEventListener ( "keydown" , down ) ;
80+ document . addEventListener ( "keydown" , handleKeyDown ) ;
81+ return ( ) => document . removeEventListener ( "keydown" , handleKeyDown ) ;
7782 } , [ ] ) ;
7883
79- const openRandomScript = async ( ) => {
84+ const handleOpenRandomScript = async ( ) => {
8085 if ( links . length === 0 ) {
8186 setIsLoading ( true ) ;
8287 try {
8388 const categories = await fetchCategories ( ) ;
8489 setLinks ( categories ) ;
85- const randomScript = getRandomScript ( categories ) ;
90+ const randomScript = getRandomScript ( categories , selectedScripts ) ;
8691 if ( randomScript ) {
92+ setSelectedScripts ( prev => new Set ( [ ...prev , randomScript . slug ] ) ) ;
8793 router . push ( `/scripts?id=${ randomScript . slug } ` ) ;
8894 }
8995 }
@@ -92,13 +98,54 @@ export default function CommandMenu() {
9298 }
9399 }
94100 else {
95- const randomScript = getRandomScript ( links ) ;
101+ const randomScript = getRandomScript ( links , selectedScripts ) ;
96102 if ( randomScript ) {
103+ setSelectedScripts ( prev => new Set ( [ ...prev , randomScript . slug ] ) ) ;
97104 router . push ( `/scripts?id=${ randomScript . slug } ` ) ;
98105 }
99106 }
100107 } ;
101108
109+ const getUniqueScriptsMap = React . useCallback ( ( ) => {
110+ const scriptMap = new Map < string , { script : Script ; categoryName : string } > ( ) ;
111+ for ( const category of links ) {
112+ for ( const script of category . scripts ) {
113+ if ( ! scriptMap . has ( script . slug ) ) {
114+ scriptMap . set ( script . slug , { script, categoryName : category . name } ) ;
115+ }
116+ }
117+ }
118+ return scriptMap ;
119+ } , [ links ] ) ;
120+
121+ const getUniqueScriptsByCategory = React . useCallback ( ( ) => {
122+ const scriptMap = getUniqueScriptsMap ( ) ;
123+ const categoryOrder = links . map ( cat => cat . name ) ;
124+ const grouped : Record < string , Script [ ] > = { } ;
125+
126+ for ( const name of categoryOrder ) {
127+ grouped [ name ] = [ ] ;
128+ }
129+
130+ for ( const { script, categoryName } of scriptMap . values ( ) ) {
131+ if ( grouped [ categoryName ] ) {
132+ grouped [ categoryName ] . push ( script ) ;
133+ }
134+ else {
135+ grouped [ categoryName ] = [ script ] ;
136+ }
137+ }
138+
139+ Object . keys ( grouped ) . forEach ( ( cat ) => {
140+ if ( grouped [ cat ] . length === 0 )
141+ delete grouped [ cat ] ;
142+ } ) ;
143+
144+ return grouped ;
145+ } , [ getUniqueScriptsMap , links ] ) ;
146+
147+ const uniqueScriptsByCategory = getUniqueScriptsByCategory ( ) ;
148+
102149 return (
103150 < >
104151 < div className = "flex gap-2" >
@@ -122,7 +169,20 @@ export default function CommandMenu() {
122169 < TooltipProvider >
123170 < Tooltip delayDuration = { 100 } >
124171 < TooltipTrigger asChild >
125- < Button variant = "outline" size = "icon" onClick = { openRandomScript } disabled = { isLoading } className = "hidden lg:flex" >
172+ < Button
173+ variant = "outline"
174+ size = "icon"
175+ onClick = { handleOpenRandomScript }
176+ disabled = { isLoading }
177+ className = "hidden lg:flex"
178+ aria-label = "Open Random Script"
179+ tabIndex = { 0 }
180+ onKeyDown = { ( e ) => {
181+ if ( e . key === "Enter" || e . key === " " ) {
182+ handleOpenRandomScript ( ) ;
183+ }
184+ } }
185+ >
126186 < Sparkles className = "size-4" />
127187 < span className = "sr-only" > Open Random Script</ span >
128188 </ Button >
@@ -139,16 +199,24 @@ export default function CommandMenu() {
139199 < CommandInput placeholder = "Search for a script..." />
140200 < CommandList >
141201 < CommandEmpty > { isLoading ? "Loading..." : "No scripts found." } </ CommandEmpty >
142- { links . map ( category => (
143- < CommandGroup key = { `category:${ category . name } ` } heading = { category . name } >
144- { category . scripts . map ( script => (
202+ { Object . entries ( uniqueScriptsByCategory ) . map ( ( [ categoryName , scripts ] ) => (
203+ < CommandGroup key = { `category:${ categoryName } ` } heading = { categoryName } >
204+ { scripts . map ( script => (
145205 < CommandItem
146206 key = { `script:${ script . slug } ` }
147- value = { `${ script . slug } -${ script . name } ` }
207+ value = { `${ script . name } -${ script . type } ` }
148208 onSelect = { ( ) => {
149209 setOpen ( false ) ;
150210 router . push ( `/scripts?id=${ script . slug } ` ) ;
151211 } }
212+ tabIndex = { 0 }
213+ aria-label = { `Open script ${ script . name } ` }
214+ onKeyDown = { ( e ) => {
215+ if ( e . key === "Enter" || e . key === " " ) {
216+ setOpen ( false ) ;
217+ router . push ( `/scripts?id=${ script . slug } ` ) ;
218+ }
219+ } }
152220 >
153221 < div className = "flex gap-2" onClick = { ( ) => setOpen ( false ) } >
154222 < Image
@@ -172,3 +240,5 @@ export default function CommandMenu() {
172240 </ >
173241 ) ;
174242}
243+
244+ export default CommandMenu ;
0 commit comments