1
1
import React , { useEffect , useState } from 'react' ;
2
- import { Card , CardContent , IconButton , Alert , Stack , Button , Typography , Grid2 , Select , MenuItem , FormControl , InputLabel , Switch , FormGroup , FormControlLabel , Dialog , DialogTitle , DialogContent , Checkbox , Badge , BadgeProps , Link , TextField , Tabs , Tab , Tooltip , InputAdornment } from '@mui/material' ;
2
+ import { Card , CardContent , IconButton , Alert , Stack , Button , Typography , Grid2 , Select , MenuItem , FormControl , InputLabel , Switch , FormGroup , FormControlLabel , Dialog , DialogTitle , DialogContent , Checkbox , Badge , BadgeProps , Link , TextField , Tabs , Tab , Tooltip , InputAdornment , CircularProgress } from '@mui/material' ;
3
3
import { CatalogItemWithName , CatalogItemCard , CatalogItem } from './PromptCard' ;
4
4
import AddIcon from '@mui/icons-material/Add' ;
5
5
import { Ref } from '../Refs' ;
@@ -8,7 +8,7 @@ import { parse, stringify } from 'yaml';
8
8
import { getRegistry } from '../Registry' ;
9
9
import { FolderOpenRounded , Search , Settings } from '@mui/icons-material' ;
10
10
import { tryRunImageSync } from '../FileWatcher' ;
11
- import { CATALOG_URL , MCP_POLICY_NAME , POLL_INTERVAL } from '../Constants' ;
11
+ import { CATALOG_URL , DD_BUILD_WITH_SECRET_SUPPORT , MCP_POLICY_NAME , POLL_INTERVAL } from '../Constants' ;
12
12
import { SecretList } from './SecretList' ;
13
13
import Secrets from '../Secrets' ;
14
14
@@ -21,9 +21,17 @@ interface CatalogGridProps {
21
21
settingsBadgeProps : BadgeProps ;
22
22
}
23
23
24
- const filterCatalog = ( catalogItems : CatalogItemWithName [ ] , registryItems : { [ key : string ] : { ref : string } } , showRegistered : boolean , showUnregistered : boolean , search : string ) =>
25
- catalogItems . filter ( ( item ) => ( showRegistered || ! Object . keys ( registryItems ) . includes ( item . name ) ) && ( showUnregistered || Object . keys ( registryItems ) . includes ( item . name ) ) && ( item . name . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ) ) ;
24
+ const filterCatalog = ( catalogItems : CatalogItemWithName [ ] , registryItems : { [ key : string ] : { ref : string } } , search : string ) =>
25
+ catalogItems . filter ( ( item ) => item . name . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ) ;
26
26
27
+ const parseDDVersion = ( ddVersion : string ) => {
28
+ //eg: Docker Desktop 4.40.0 (184396)
29
+ const [ , , version , build ] = ddVersion . split ( ' ' ) ;
30
+ return {
31
+ version,
32
+ build : parseInt ( build . replace ( '(' , '' ) . replace ( ')' , '' ) )
33
+ }
34
+ }
27
35
const NEVER_SHOW_AGAIN_KEY = 'registry-sync-never-show-again' ;
28
36
29
37
export const CatalogGrid : React . FC < CatalogGridProps > = ( {
@@ -35,14 +43,13 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
35
43
settingsBadgeProps
36
44
} ) => {
37
45
const [ catalogItems , setCatalogItems ] = useState < CatalogItemWithName [ ] > ( [ ] ) ;
38
- const [ showRegistered , setShowRegistered ] = useState < boolean > ( true ) ;
39
- const [ showUnregistered , setShowUnregistered ] = useState < boolean > ( true ) ;
40
46
const [ showReloadModal , setShowReloadModal ] = useState < boolean > ( false ) ;
41
47
const [ search , setSearch ] = useState < string > ( '' ) ;
42
48
const [ tab , setTab ] = useState < number > ( 0 ) ;
43
49
const [ secrets , setSecrets ] = useState < Secrets . Secret [ ] > ( [ ] ) ;
50
+ const [ ddVersion , setDdVersion ] = useState < { version : string , build : number } | null > ( null ) ;
44
51
45
- const filteredCatalogItems = filterCatalog ( catalogItems , registryItems , showRegistered , showUnregistered , search ) ;
52
+ const filteredCatalogItems = filterCatalog ( catalogItems , registryItems , search ) ;
46
53
47
54
const loadCatalog = async ( showNotification = true ) => {
48
55
const cachedCatalog = localStorage . getItem ( 'catalog' ) ;
@@ -51,7 +58,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
51
58
const catalog = await response . text ( ) ;
52
59
const items = parse ( catalog ) [ 'registry' ] as { [ key : string ] : CatalogItem }
53
60
const itemsWithName = Object . entries ( items ) . map ( ( [ name , item ] ) => ( { name, ...item } ) ) ;
54
- const filteredItems = filterCatalog ( itemsWithName , registryItems , showRegistered , showUnregistered , search ) ;
61
+ const filteredItems = filterCatalog ( itemsWithName , registryItems , search ) ;
55
62
setCatalogItems ( filteredItems ) ;
56
63
localStorage . setItem ( 'catalog' , JSON . stringify ( filteredItems ) ) ;
57
64
if ( showNotification ) {
@@ -73,7 +80,12 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
73
80
setSecrets ( response || [ ] ) ;
74
81
}
75
82
76
- const registerCatalogItem = async ( item : CatalogItemWithName ) => {
83
+ const loadDDVersion = async ( ) => {
84
+ const ddVersionResult = await client . docker . cli . exec ( 'version' , [ '--format' , 'json' ] )
85
+ setDdVersion ( parseDDVersion ( JSON . parse ( ddVersionResult . stdout ) . Server . Platform . Name ) ) ;
86
+ }
87
+
88
+ const registerCatalogItem = async ( item : CatalogItemWithName , showNotification = true ) => {
77
89
try {
78
90
const currentRegistry = await getRegistry ( client ) ;
79
91
const newRegistry = { ...currentRegistry , [ item . name ] : { ref : item . ref } } ;
@@ -84,12 +96,18 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
84
96
} ]
85
97
} )
86
98
await tryRunImageSync ( client , [ '--rm' , '-v' , 'docker-prompts:/docker-prompts' , '--workdir' , '/docker-prompts' , 'vonwig/function_write_files:latest' , `'${ payload } '` ] )
87
- client . desktopUI . toast . success ( 'Prompt registered successfully. Restart Claude Desktop to apply.' ) ;
99
+ if ( showNotification ) {
100
+ client . desktopUI . toast . success ( 'Prompt registered successfully. Restart Claude Desktop to apply.' ) ;
101
+ }
88
102
onRegistryChange ( ) ;
89
- setShowReloadModal ( ! localStorage . getItem ( NEVER_SHOW_AGAIN_KEY ) ) ;
103
+ if ( showNotification ) {
104
+ setShowReloadModal ( ! localStorage . getItem ( NEVER_SHOW_AGAIN_KEY ) ) ;
105
+ }
90
106
}
91
107
catch ( error ) {
92
- client . desktopUI . toast . error ( 'Failed to register prompt: ' + error ) ;
108
+ if ( showNotification ) {
109
+ client . desktopUI . toast . error ( 'Failed to register prompt: ' + error ) ;
110
+ }
93
111
}
94
112
}
95
113
@@ -116,6 +134,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
116
134
useEffect ( ( ) => {
117
135
loadCatalog ( false ) ;
118
136
loadSecrets ( ) ;
137
+ loadDDVersion ( ) ;
119
138
const interval = setInterval ( ( ) => {
120
139
loadCatalog ( false ) ;
121
140
loadSecrets ( ) ;
@@ -129,6 +148,10 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
129
148
catalogItems . some ( ( c ) => c . name === i )
130
149
)
131
150
151
+ if ( ! ddVersion ) {
152
+ return < CircularProgress />
153
+ }
154
+
132
155
133
156
return (
134
157
< Stack spacing = { 2 } justifyContent = 'center' alignItems = 'center' >
@@ -188,6 +211,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
188
211
client . host . openExternal ( Ref . fromRef ( item . ref ) . toURL ( true ) ) ;
189
212
} }
190
213
item = { item }
214
+ ddVersion = { ddVersion }
191
215
canRegister = { canRegister }
192
216
registered = { Object . keys ( registryItems ) . some ( ( i ) => i === item . name ) }
193
217
register = { registerCatalogItem }
@@ -215,7 +239,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
215
239
{ tab === 1 && < Grid2 container spacing = { 1 } width = '90vw' maxWidth = { 1000 } >
216
240
{ Object . entries ( registryItems ) . map ( ( [ name , item ] ) => (
217
241
name . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) && < Grid2 size = { { xs : 12 , sm : 6 , md : 4 } } key = { name } >
218
- < CatalogItemCard item = { catalogItems . find ( ( i ) => i . name === name ) ! } openUrl = { ( ) => {
242
+ < CatalogItemCard ddVersion = { ddVersion } item = { catalogItems . find ( ( i ) => i . name === name ) ! } openUrl = { ( ) => {
219
243
client . host . openExternal ( Ref . fromRef ( item . ref ) . toURL ( true ) ) ;
220
244
} } canRegister = { canRegister } registered = { true } register = { registerCatalogItem } unregister = { unregisterCatalogItem } onSecretChange = { async ( secret ) => {
221
245
await Secrets . addSecret ( client , { name : secret . name , value : secret . value , policies : [ MCP_POLICY_NAME ] } )
@@ -224,7 +248,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
224
248
</ Grid2 >
225
249
) ) }
226
250
</ Grid2 > }
227
- { tab === 2 && < SecretList secrets = { secrets } /> }
251
+ { tab === 2 && ddVersion && < SecretList secrets = { secrets } ddVersion = { ddVersion } /> }
228
252
</ Stack >
229
253
) ;
230
254
} ;
0 commit comments