/ src / renderer / components / common / ConfigStdioCard.vue
ConfigStdioCard.vue
  1  <script setup lang="ts">
  2  import { McpMetadataStdio } from '@/types/mcp'
  3  import { reactive, ref } from 'vue'
  4  import { useStdioStore } from '@/renderer/store/stdio'
  5  import { getRawServers } from '@/renderer/store/mcp'
  6  
  7  import McpEditPage from '@/renderer/components/pages/McpEditPage.vue'
  8  
  9  import MarkdownCard from '@/renderer/components/common/MarkdownCard.vue'
 10  
 11  const stdioStore = useStdioStore()
 12  
 13  const editDialog = ref(false)
 14  
 15  const show = ref(false)
 16  
 17  const showPassword = reactive<Record<string, boolean>>({})
 18  
 19  const props = defineProps({
 20    modelValue: {
 21      type: Object as () => McpMetadataStdio,
 22      required: true
 23    }
 24  })
 25  
 26  const { modelValue: metadata } = props
 27  
 28  function formattedValue(value: string[], sep: string) {
 29    if (Array.isArray(value)) {
 30      return value.join(sep)
 31    } else {
 32      return value
 33    }
 34  }
 35  
 36  const editConfig = () => {
 37    editDialog.value = true
 38  }
 39  </script>
 40  
 41  <template>
 42    <v-card :title="$t('mcp.stdio')">
 43      <template v-if="metadata.description?.implementation" #subtitle>
 44        {{ metadata.description?.implementation.title ?? metadata.description?.implementation.name }}
 45      </template>
 46      <template v-if="metadata.description?.implementation.version" #append>
 47        <v-chip size="small" class="font-weight-bold" color="primary">{{
 48          metadata.description.implementation.version
 49        }}</v-chip>
 50      </template>
 51      <v-divider></v-divider>
 52      <v-card-text>
 53        <div v-for="(value, key) in metadata.config" :key="key" class="ma-2">
 54          <div v-if="key === 'command'">
 55            <v-text-field
 56              prepend-icon="mdi-application-outline"
 57              :model-value="value"
 58              density="compact"
 59              variant="outlined"
 60              readonly
 61              hide-details
 62            ></v-text-field>
 63          </div>
 64          <div v-else-if="key === 'args'">
 65            <v-divider class="mt-4"></v-divider>
 66            <v-textarea
 67              class="mt-4"
 68              prepend-icon="mdi-application-brackets-outline"
 69              :model-value="formattedValue(value as string[], ' \n')"
 70              density="compact"
 71              variant="outlined"
 72              readonly
 73              rows="1"
 74              auto-grow
 75            ></v-textarea>
 76            <v-text-field
 77              prepend-icon="mdi-application-export"
 78              :model-value="metadata.config['command'] + ' ' + formattedValue(value as string[], ' ')"
 79              density="compact"
 80              variant="outlined"
 81              readonly
 82              hide-details
 83            ></v-text-field>
 84          </div>
 85          <div v-else-if="key === 'env'">
 86            <v-divider class="mt-4"></v-divider>
 87  
 88            <v-text-field
 89              v-for="(envValue, envKey) in value as Record<string, string>"
 90              :key="envKey"
 91              class="mt-4"
 92              prepend-icon="mdi-application-braces-outline"
 93              :type="showPassword[envKey] ? 'text' : 'password'"
 94              density="compact"
 95              variant="outlined"
 96              :append-inner-icon="showPassword[envKey] ? 'mdi-eye-off' : 'mdi-eye'"
 97              :label="envKey"
 98              :model-value="envValue"
 99              readonly
100              hide-details
101              @click:append-inner="showPassword[envKey] = !showPassword[envKey]"
102            >
103            </v-text-field>
104          </div>
105          <div v-else>
106            <v-divider class="mt-4"></v-divider>
107            <v-text-field
108              class="mt-4"
109              prepend-icon="mdi-cog-outline"
110              :model-value="value"
111              density="compact"
112              variant="outlined"
113              readonly
114              hide-details
115            ></v-text-field>
116          </div>
117        </div>
118      </v-card-text>
119      <v-divider></v-divider>
120      <v-card-actions>
121        <v-btn
122          v-if="metadata.description?.instructions"
123          :icon="show ? 'mdi-chevron-up' : 'mdi-chevron-down'"
124          @click="show = !show"
125        ></v-btn>
126        <v-spacer> </v-spacer>
127        <v-btn
128          v-if="metadata.name in (getRawServers() ?? {})"
129          v-tooltip:top="$t('general.reset')"
130          icon="mdi-refresh"
131          rounded="lg"
132          color="error"
133          @click="stdioStore.deleteConfig(metadata.name)"
134        >
135        </v-btn>
136        <v-btn
137          v-else
138          v-tooltip:top="$t('general.delete')"
139          icon="mdi-delete-outline"
140          rounded="lg"
141          color="error"
142          @click="stdioStore.deleteConfig(metadata.name)"
143        >
144        </v-btn>
145  
146        <v-btn
147          v-tooltip:top="$t('general.edit')"
148          icon="mdi-lead-pencil"
149          rounded="lg"
150          color="primary"
151          @click="editConfig"
152        >
153        </v-btn>
154      </v-card-actions>
155      <v-expand-transition v-if="metadata.description?.instructions">
156        <div v-show="show">
157          <v-divider></v-divider>
158          <v-card-text class="mx-2">
159            <MarkdownCard :model-value="metadata.description.instructions"></MarkdownCard
160          ></v-card-text>
161        </div>
162      </v-expand-transition>
163    </v-card>
164    <McpEditPage v-model="editDialog" v-model:name="metadata.name"></McpEditPage>
165  </template>