@@ -5,7 +5,7 @@ import fuzzysort from "fuzzysort"
55import { createEffect , createMemo , createSignal , type Accessor } from "solid-js"
66import { RunFooterMenu , createFooterMenuState , type RunFooterMenuItem } from "./footer.menu"
77import type { RunFooterTheme } from "./theme"
8- import type { FooterSubagentTab , RunCommand , RunInput , RunProvider } from "./types"
8+ import type { FooterQueuedPrompt , FooterSubagentTab , RunCommand , RunInput , RunProvider } from "./types"
99
1010type PanelEntry = RunFooterMenuItem & {
1111 category : string
@@ -14,6 +14,7 @@ type PanelEntry = RunFooterMenuItem & {
1414
1515type CommandEntry =
1616 | ( PanelEntry & { action : "model" } )
17+ | ( PanelEntry & { action : "queued" } )
1718 | ( PanelEntry & { action : "subagent" } )
1819 | ( PanelEntry & { action : "variant.cycle" } )
1920 | ( PanelEntry & { action : "variant.list" } )
@@ -37,6 +38,10 @@ type SubagentEntry = PanelEntry & {
3738 current : boolean
3839}
3940
41+ type QueuedEntry = PanelEntry & {
42+ prompt : FooterQueuedPrompt
43+ }
44+
4045type MenuState = ReturnType < typeof createFooterMenuState >
4146
4247const PANEL_PAD = 2
@@ -294,11 +299,13 @@ export function RunCommandMenuBody(props: {
294299 theme : Accessor < RunFooterTheme >
295300 commands : Accessor < RunCommand [ ] | undefined >
296301 subagents : Accessor < FooterSubagentTab [ ] >
302+ queued : Accessor < FooterQueuedPrompt [ ] >
297303 variants : Accessor < string [ ] >
298304 variantCycle : string
299305 onClose : ( ) => void
300306 onModel : ( ) => void
301307 onSubagent : ( ) => void
308+ onQueued : ( ) => void
302309 onVariant : ( ) => void
303310 onVariantCycle : ( ) => void
304311 onCommand : ( name : string ) => void
@@ -315,6 +322,20 @@ export function RunCommandMenuBody(props: {
315322 category : "Suggested" ,
316323 display : "Switch model" ,
317324 } ,
325+ ...( props . queued ( ) . length > 0
326+ ? [
327+ {
328+ action : "queued" as const ,
329+ category : "Suggested" ,
330+ display : "Manage queued prompts" ,
331+ footer : `${ props . queued ( ) . length } queued` ,
332+ keywords : props
333+ . queued ( )
334+ . map ( ( item ) => item . prompt . text )
335+ . join ( " " ) ,
336+ } ,
337+ ]
338+ : [ ] ) ,
318339 ...( props . subagents ( ) . length > 0
319340 ? [
320341 {
@@ -387,6 +408,11 @@ export function RunCommandMenuBody(props: {
387408 return
388409 }
389410
411+ if ( item . action === "queued" ) {
412+ props . onQueued ( )
413+ return
414+ }
415+
390416 if ( item . action === "variant.cycle" ) {
391417 props . onVariantCycle ( )
392418 return
@@ -559,6 +585,102 @@ export function RunSubagentSelectBody(props: {
559585 )
560586}
561587
588+ export function RunQueuedPromptSelectBody ( props : {
589+ theme : Accessor < RunFooterTheme >
590+ prompts : Accessor < FooterQueuedPrompt [ ] >
591+ onClose : ( ) => void
592+ onEdit : ( prompt : FooterQueuedPrompt ) => void | Promise < void >
593+ onDelete : ( prompt : FooterQueuedPrompt ) => void | Promise < void >
594+ onRows ?: ( rows : number ) => void
595+ } ) {
596+ let field : InputRenderable | undefined
597+ const [ query , setQuery ] = createSignal ( "" )
598+ const entries = createMemo < QueuedEntry [ ] > ( ( ) =>
599+ props . prompts ( ) . map ( ( prompt ) => ( {
600+ category : "" ,
601+ display : prompt . prompt . text . replaceAll ( "\n" , " " ) ,
602+ footer : "queued · ctrl+e edit · ctrl+d remove" ,
603+ keywords : prompt . prompt . text ,
604+ prompt,
605+ } ) ) ,
606+ )
607+ const items = createMemo < QueuedEntry [ ] > ( ( ) => match ( query ( ) , entries ( ) ) )
608+ const menu = createFooterMenuState ( { count : ( ) => items ( ) . length , limit : SUBAGENT_LIST_ROWS } )
609+ const selected = ( ) => items ( ) [ menu . selected ( ) ]
610+
611+ createEffect ( ( ) => {
612+ query ( )
613+ menu . reset ( )
614+ } )
615+
616+ createEffect ( ( ) => {
617+ props . onRows ?.( menu . rows ( ) + PANEL_FRAME_ROWS )
618+ } )
619+
620+ useKeyboard ( ( event ) => {
621+ if ( event . defaultPrevented ) {
622+ return
623+ }
624+
625+ const item = selected ( )
626+ const ctrl = event . ctrl && ! event . meta && ! event . shift && ! event . super
627+ if ( item && ( event . name === "delete" || ( ctrl && event . name === "d" ) ) ) {
628+ event . preventDefault ( )
629+ props . onDelete ( item . prompt )
630+ return
631+ }
632+
633+ if ( item && ctrl && event . name === "e" ) {
634+ event . preventDefault ( )
635+ props . onEdit ( item . prompt )
636+ return
637+ }
638+
639+ handleKey ( {
640+ event,
641+ menu,
642+ field : ( ) => field ,
643+ setQuery,
644+ select : ( ) => {
645+ const item = selected ( )
646+ if ( item ) props . onEdit ( item . prompt )
647+ } ,
648+ close : props . onClose ,
649+ } )
650+ } )
651+
652+ return (
653+ < PanelShell
654+ id = "run-direct-footer-queued-panel"
655+ title = "Queued prompts"
656+ query = { query ( ) }
657+ count = { items ( ) . length }
658+ total = { entries ( ) . length }
659+ placeholder = "Search"
660+ theme = { props . theme }
661+ inputRef = { ( input ) => {
662+ field = input
663+ } }
664+ onQuery = { setQuery }
665+ >
666+ < RunFooterMenu
667+ id = "run-direct-footer-queued-list"
668+ theme = { props . theme }
669+ items = { items }
670+ selected = { menu . selected }
671+ offset = { menu . offset }
672+ rows = { menu . rows }
673+ limit = { SUBAGENT_LIST_ROWS }
674+ empty = "No queued prompts"
675+ border = { false }
676+ paddingLeft = { PANEL_PAD }
677+ paddingRight = { PANEL_PAD }
678+ grouped = { false }
679+ />
680+ </ PanelShell >
681+ )
682+ }
683+
562684export function RunVariantSelectBody ( props : {
563685 theme : Accessor < RunFooterTheme >
564686 variants : Accessor < string [ ] >
0 commit comments