McpPromptPage.vue
1 <script setup lang="ts"> 2 import { useMessageStore } from '@/renderer/store/message' 3 import { useLayoutStore } from '@/renderer/store/layout' 4 import { usePromptStore } from '@/renderer/store/prompt' 5 import { useSnackbarStore } from '@/renderer/store/snackbar' 6 import { useRouter } from 'vue-router' 7 import { ref } from 'vue' 8 import { useRules } from '@/renderer/composables/useRules' 9 10 const messageStore = useMessageStore() 11 const promptStore = usePromptStore() 12 const layoutStore = useLayoutStore() 13 const snackbarStore = useSnackbarStore() 14 15 const router = useRouter() 16 17 const rules = useRules() 18 19 const isValid = ref(true) 20 21 const navigateTo = (route: string, screenValue: number) => { 22 layoutStore.screen = screenValue 23 router.push(route) 24 } 25 26 const handleApplyPrompt = async () => { 27 try { 28 const conversations = await promptStore.fetchSelect() 29 30 if (!conversations || conversations.length === 0) { 31 snackbarStore.showErrorMessage( 32 'Prompt not found — possible MCP server issue or protocol mismatch, please contact the repo owner.' 33 ) 34 } 35 messageStore.initConversation(conversations) 36 navigateTo('/chat', 1) 37 } catch (error) { 38 const errorMsg = error instanceof Error ? error.message : String(error) 39 console.log(errorMsg) 40 snackbarStore.showErrorMessage(errorMsg) 41 } 42 } 43 </script> 44 45 <template> 46 <v-data-iterator 47 :items="promptStore.promptList" 48 :search="promptStore.search" 49 items-per-page="-1" 50 :loading="promptStore.loading" 51 @update:options="promptStore.loadPrompts" 52 > 53 <template #header> 54 <v-toolbar class="px-2" rounded="lg"> 55 <v-text-field 56 v-model="promptStore.search" 57 density="compact" 58 placeholder="Search" 59 prepend-inner-icon="mdi-magnify" 60 variant="solo" 61 clearable 62 hide-details 63 ></v-text-field> 64 </v-toolbar> 65 </template> 66 <template #default="{ items }"> 67 <v-container class="pa-2" fluid> 68 <v-row density="compact"> 69 <v-col 70 v-for="item in items" 71 :key="item.raw.title + ':' + item.raw.name" 72 cols="auto" 73 class="flex-fill" 74 > 75 <v-card border flat> 76 <v-card-item :subtitle="item.raw.name" class="mb-2" :title="item.raw.title"> 77 <template #append> 78 <v-btn 79 icon="mdi-lead-pencil" 80 size="small" 81 text="Read" 82 border 83 flat 84 @click="promptStore.select(item.raw)" 85 > 86 </v-btn> 87 </template> 88 </v-card-item> 89 <v-card-text>{{ item.raw.description }}</v-card-text> 90 </v-card> 91 </v-col> 92 </v-row> 93 </v-container> 94 </template> 95 </v-data-iterator> 96 97 <v-dialog v-model="promptStore.promptSheet" max-width="80vw" max-height="80vh" scrollable> 98 <v-card 99 prepend-icon="mdi-account-cog-outline" 100 :title="$t('prompt.title') + ' - ' + promptStore.promptSelect.title" 101 :subtitle="promptStore.promptSelect.name" 102 > 103 <v-divider></v-divider> 104 <v-card-text class="pt-0"> 105 <v-textarea 106 variant="plain" 107 :model-value="promptStore.promptSelect.description" 108 rows="1" 109 auto-grow 110 hide-details 111 ></v-textarea> 112 <v-form v-if="promptStore.promptSelect.arguments" ref="form" v-model="isValid" class="pt-2"> 113 <v-textarea 114 v-for="argument in promptStore.promptSelect.arguments" 115 :key="argument.name" 116 v-model="argument.content" 117 :hint="argument.description" 118 class="mx-2 mt-3" 119 :base-color="argument.required ? 'secondary' : 'primary'" 120 :color="argument.required ? 'secondary' : 'primary'" 121 type="text" 122 variant="outlined" 123 :label="argument.name" 124 rows="1" 125 auto-grow 126 :rules="argument.required ? [rules.required] : []" 127 > 128 </v-textarea> 129 </v-form> 130 </v-card-text> 131 <v-divider></v-divider> 132 <v-card-actions> 133 <v-spacer></v-spacer> 134 <v-btn 135 class="my-2 mx-3 px-3" 136 variant="tonal" 137 :disabled="!isValid" 138 @click="handleApplyPrompt" 139 > 140 {{ $t('prompt.get') }} 141 <template #prepend> 142 <v-icon v-if="!isValid" icon="mdi-close-circle" color="error"></v-icon> 143 <v-icon v-else icon="mdi-check-circle" color="success"></v-icon> 144 </template> 145 </v-btn> 146 </v-card-actions> 147 </v-card> 148 </v-dialog> 149 </template>