@@ -19,6 +19,29 @@ import RelatedSkills from "../components/skill/RelatedSkills";
1919import { copyToClipboard } from "../lib/utils" ;
2020import { DetailSkeleton } from "../components/loading" ;
2121
22+ type IdeType = 'cursor' | 'kiro' | 'qoder' | 'lingma' | 'claude' | 'codex' ;
23+
24+ const IDE_OPTIONS : { value : IdeType ; label : string ; icon : string } [ ] = [
25+ { value : 'cursor' , label : 'Cursor' , icon : 'https://aimg.alistatic.com/i/dm885X/UgW1Qzx0RPwrNIVs4sWlr-7b5511235d.svg' } ,
26+ { value : 'claude' , label : 'Claude' , icon : 'https://img.alicdn.com/imgextra/i3/O1CN01JqyNKC1VmMU2MHdF9_!!6000000002695-55-tps-100-101.svg' } ,
27+ { value : 'qoder' , label : 'Qoder' , icon : 'https://g.alicdn.com/qbase/qoder/0.0.65/favIcon.svg' } ,
28+ { value : 'codex' , label : 'Codex' , icon : 'https://img.alicdn.com/imgextra/i3/O1CN011DvgjK1s54F8K000Q_!!6000000005714-0-tps-248-248.jpg' } ,
29+ { value : 'kiro' , label : 'Kiro' , icon : '/kiro.png' } ,
30+ { value : 'lingma' , label : 'Lingma' , icon : 'https://img.alicdn.com/imgextra/i2/O1CN01OR7j0c1OvKuJfKBAw_!!6000000001767-2-tps-280-280.png' } ,
31+ ] ;
32+
33+ const getDefaultOutputDir = ( ide : IdeType ) : string => {
34+ const dirMap : Record < IdeType , string > = {
35+ cursor : './.cursor/skills' ,
36+ kiro : './.kiro/skills' ,
37+ qoder : './.qoder/skills' ,
38+ lingma : './.lingma/skills' ,
39+ claude : './.claude/skills' ,
40+ codex : './.codex/skills' ,
41+ } ;
42+ return dirMap [ ide ] ;
43+ } ;
44+
2245function inferLanguage ( path : string ) : string {
2346 const fileName = path . split ( "/" ) . pop ( ) ?. toLowerCase ( ) ?? "" ;
2447 if ( fileName === "dockerfile" ) return "dockerfile" ;
@@ -85,6 +108,8 @@ function SkillDetail() {
85108 const [ versions , setVersions ] = useState < SkillVersion [ ] > ( [ ] ) ;
86109 const [ selectedVersion , setSelectedVersion ] = useState < string | undefined > ( ) ;
87110 const [ cliInfo , setCliInfo ] = useState < SkillCliInfo | null > ( null ) ;
111+ const [ selectedIde , setSelectedIde ] = useState < IdeType > ( 'qoder' ) ;
112+ const [ outputDir , setOutputDir ] = useState < string > ( './.qoder/skills' ) ;
88113
89114 const handleDragStart = ( e : React . MouseEvent ) => {
90115 e . preventDefault ( ) ;
@@ -554,14 +579,14 @@ function SkillDetail() {
554579 { /* Nacos CLI command */ }
555580 { cliInfo && (
556581 < div className = "px-4 py-3" >
557- < div className = "flex items-center justify-between mb-2 " >
582+ < div className = "flex items-center justify-between mb-3 " >
558583 < div className = "flex items-center gap-1.5" >
559584 < CodeOutlined className = "text-gray-400 text-xs" />
560585 < span className = "text-xs font-medium text-gray-500" > NPX 下载</ span >
561586 </ div >
562587 < button
563588 onClick = { ( ) => {
564- const cmd = `npx @nacos-group/cli --host ${ cliInfo . nacosHost } skill-get ${ cliInfo . resourceName } ` ;
589+ const cmd = `npx @nacos-group/cli --host ${ cliInfo . nacosHost } skill-get ${ cliInfo . resourceName } -o ${ outputDir } ` ;
565590 copyToClipboard ( cmd ) . then ( ( ) => {
566591 setCopied ( true ) ;
567592 setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
@@ -572,9 +597,52 @@ function SkillDetail() {
572597 { copied ? < CheckOutlined className = "text-green-500" /> : < CopyOutlined /> }
573598 </ button >
574599 </ div >
600+
601+ { /* IDE Selection */ }
602+ < div className = "mb-3" >
603+ < div className = "text-xs text-gray-500 mb-2" > 选择 IDE</ div >
604+ < div className = "flex flex-wrap gap-2" >
605+ { IDE_OPTIONS . map ( ( ide ) => (
606+ < button
607+ key = { ide . value }
608+ onClick = { ( ) => {
609+ setSelectedIde ( ide . value ) ;
610+ setOutputDir ( getDefaultOutputDir ( ide . value ) ) ;
611+ } }
612+ className = { `flex items-center gap-1.5 px-2.5 py-1.5 rounded-md border text-xs transition-all ${
613+ selectedIde === ide . value
614+ ? 'border-blue-500 bg-blue-50 text-blue-700'
615+ : 'border-gray-200 hover:border-gray-300 text-gray-600'
616+ } `}
617+ >
618+ { ide . icon && (
619+ < img
620+ src = { ide . icon }
621+ alt = { ide . label }
622+ className = "w-4 h-4 object-contain"
623+ />
624+ ) }
625+ < span > { ide . label } </ span >
626+ </ button >
627+ ) ) }
628+ </ div >
629+ </ div >
630+
631+ { /* Output Directory */ }
632+ < div className = "mb-3" >
633+ < div className = "text-xs text-gray-500 mb-1.5" > 输出目录</ div >
634+ < input
635+ type = "text"
636+ value = { outputDir }
637+ onChange = { ( e ) => setOutputDir ( e . target . value ) }
638+ className = "w-full px-3 py-2 text-xs border border-gray-200 rounded-md focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 bg-white text-gray-700"
639+ placeholder = "输入输出目录路径"
640+ />
641+ </ div >
642+
575643 < div className = "rounded-md bg-gray-100 border border-gray-200 px-3 py-2" >
576644 < code className = "text-[12px] text-gray-700 break-all" style = { { fontFamily : "'Menlo', 'Monaco', 'Courier New', monospace" } } >
577- { `npx @nacos-group/cli --host ${ cliInfo . nacosHost } skill-get ${ cliInfo . resourceName } ` }
645+ { `npx @nacos-group/cli --host ${ cliInfo . nacosHost } skill-get ${ cliInfo . resourceName } -o ${ outputDir } ` }
578646 </ code >
579647 </ div >
580648 </ div >
0 commit comments