@@ -5,7 +5,20 @@ import {
5
5
CardBody ,
6
6
CardFooter ,
7
7
DarkModeContext ,
8
+ Dialog ,
9
+ DialogCloseButton ,
10
+ DialogContent ,
11
+ DialogHeader ,
12
+ DialogModal ,
13
+ DialogModalOverlay ,
14
+ DialogTitle ,
15
+ DialogTrigger ,
16
+ FieldGroup ,
17
+ Input ,
18
+ Link ,
8
19
Loader ,
20
+ SearchField ,
21
+ SearchFieldClearButton ,
9
22
Text ,
10
23
} from "@stacklok/ui-kit" ;
11
24
import {
@@ -33,6 +46,9 @@ import {
33
46
import { v1GetWorkspaceCustomInstructionsQueryKey } from "@/api/generated/@tanstack/react-query.gen" ;
34
47
import { useQueryGetWorkspaceCustomInstructions } from "../hooks/use-query-get-workspace-custom-instructions" ;
35
48
import { useMutationSetWorkspaceCustomInstructions } from "../hooks/use-mutation-set-workspace-custom-instructions" ;
49
+ import { Bot , Download , Search } from "lucide-react" ;
50
+ import Fuse from "fuse.js" ;
51
+ import systemPrompts from "../constants/built-in-system-prompts.json" ;
36
52
37
53
type DarkModeContextValue = {
38
54
preference : "dark" | "light" | null ;
@@ -129,6 +145,96 @@ function useCustomInstructionsValue({
129
145
return { value, setValue } ;
130
146
}
131
147
148
+ type PromptPresetPickerProps = {
149
+ onActivate : ( text : string ) => void ;
150
+ } ;
151
+
152
+ function PromptPresetPicker ( { onActivate } : PromptPresetPickerProps ) {
153
+ const [ query , setQuery ] = useState < string > ( "" ) ;
154
+
155
+ const fuse = new Fuse ( systemPrompts , {
156
+ keys : [ "name" , "text" ] ,
157
+ } ) ;
158
+
159
+ const handleActivate = useCallback (
160
+ ( prompt : string ) => {
161
+ onActivate ( prompt ) ;
162
+ } ,
163
+ [ onActivate ] ,
164
+ ) ;
165
+
166
+ return (
167
+ < >
168
+ < DialogHeader >
169
+ < div className = "w-1/3" >
170
+ < DialogTitle > Choose a prompt template</ DialogTitle >
171
+ </ div >
172
+ < div className = "w-1/3" >
173
+ < SearchField
174
+ className = "w-full max-w-96"
175
+ value = { query }
176
+ onChange = { setQuery }
177
+ >
178
+ < FieldGroup >
179
+ < Input icon = { < Search /> } placeholder = "Type to search" autoFocus />
180
+ { query . length > 0 ? < SearchFieldClearButton /> : null }
181
+ </ FieldGroup >
182
+ </ SearchField >
183
+ </ div >
184
+ < div className = "w-1/3 flex justify-end" >
185
+ < DialogCloseButton />
186
+ </ div >
187
+ </ DialogHeader >
188
+ < DialogContent >
189
+ < div className = "grid grid-flow-row grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 overflow-auto justify-around " >
190
+ { fuse . search ( query . length > 0 ? query : " " ) . map ( ( { item } ) => {
191
+ return (
192
+ < Card className = " flex flex-col" >
193
+ < h2 className = "font-bold p-2 flex gap-2 items-center" >
194
+ < Bot className = "size-4" />
195
+ < div className = "truncate" > { item . name } </ div >
196
+ </ h2 >
197
+ < pre className = "h-72 overflow-hidden text-wrap text-sm bg-gray-50 p-2 overflow-y-auto" >
198
+ { item . text }
199
+ </ pre >
200
+ < div className = "flex gap-4 justify-between p-2" >
201
+ < div className = "h-full items-center" >
202
+ < div className = "flex h-full items-center max-w-52 text-clip" >
203
+ { item . contributors . map ( ( contributor ) => (
204
+ < Link
205
+ className = "font-bold text-sm no-underline text-secondary flex gap-1 items-center hover:bg-gray-200 h-full px-2 rounded-md"
206
+ target = "_blank"
207
+ href = { `https://github.com/${ contributor } /` }
208
+ >
209
+ < img
210
+ className = "size-6 rounded-full"
211
+ src = { `https://github.com/${ contributor } .png?size=24` }
212
+ />
213
+ < span className = "truncate" > { contributor } </ span >
214
+ </ Link >
215
+ ) ) }
216
+ </ div >
217
+ </ div >
218
+ < Button
219
+ isIcon
220
+ slot = "close"
221
+ variant = "secondary"
222
+ onPress = { ( ) => {
223
+ handleActivate ( item . text ) ;
224
+ } }
225
+ >
226
+ < Download />
227
+ </ Button >
228
+ </ div >
229
+ </ Card >
230
+ ) ;
231
+ } ) }
232
+ </ div >
233
+ </ DialogContent >
234
+ </ >
235
+ ) ;
236
+ }
237
+
132
238
export function WorkspaceCustomInstructions ( {
133
239
className,
134
240
workspaceName,
@@ -209,6 +315,24 @@ export function WorkspaceCustomInstructions({
209
315
</ div >
210
316
</ CardBody >
211
317
< CardFooter className = "justify-end gap-2" >
318
+ < DialogTrigger >
319
+ < Button > Use a preset</ Button >
320
+ < DialogModalOverlay isDismissable >
321
+ < DialogModal isDismissable >
322
+ < Dialog
323
+ width = "lg"
324
+ className = "flex flex-col p-4 gap-4 "
325
+ style = { { maxWidth : "min(calc(100vw - 64px), 1200px)" } }
326
+ >
327
+ < PromptPresetPicker
328
+ onActivate = { ( prompt : string ) => {
329
+ setValue ( prompt ) ;
330
+ } }
331
+ />
332
+ </ Dialog >
333
+ </ DialogModal >
334
+ </ DialogModalOverlay >
335
+ </ DialogTrigger >
212
336
< Button
213
337
isPending = { isMutationPending }
214
338
isDisabled = { Boolean ( isArchived ?? isCustomInstructionsPending ) }
0 commit comments