完成 prompts,但是还有点 bug
This commit is contained in:
parent
2f104cda6d
commit
5564dec451
@ -1,14 +1,34 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="lexicon-module">
|
<div class="prompt-module">
|
||||||
<h2>题词模块</h2>
|
<div class="left">
|
||||||
<!-- 题词模块内容将在这里实现 -->
|
<h2>
|
||||||
</div>
|
<span class="iconfont icon-chat"></span>
|
||||||
|
提示词模块
|
||||||
|
</h2>
|
||||||
|
<h3><code>prompts/list</code></h3>
|
||||||
|
|
||||||
|
<PromptTemplates
|
||||||
|
:tab-id="props.tabId"
|
||||||
|
></PromptTemplates>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<PromptReader
|
||||||
|
:tab-id="props.tabId"
|
||||||
|
></PromptReader>
|
||||||
|
|
||||||
|
<PromptLogger
|
||||||
|
:tab-id="props.tabId"
|
||||||
|
></PromptLogger>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, defineProps } from 'vue';
|
import { defineProps } from 'vue';
|
||||||
|
import PromptTemplates from './prompt-templates.vue';
|
||||||
defineComponent({ name: 'prompt' });
|
import PromptReader from './prompt-reader.vue';
|
||||||
|
import PromptLogger from './prompt-logger.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabId: {
|
tabId: {
|
||||||
@ -20,8 +40,19 @@ const props = defineProps({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.lexicon-module {
|
.prompt-module {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-module .left {
|
||||||
|
width: 45%;
|
||||||
|
max-width: 410px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-module .right {
|
||||||
|
width: 45%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
104
app/src/components/main-panel/prompt/prompt-logger.vue
Normal file
104
app/src/components/main-panel/prompt/prompt-logger.vue
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<div class="prompt-logger">
|
||||||
|
<span>
|
||||||
|
<span>{{ t('response') }}</span>
|
||||||
|
<span style="width: 200px;">
|
||||||
|
<el-switch
|
||||||
|
v-model="showRawJson"
|
||||||
|
inline-prompt
|
||||||
|
active-text="JSON"
|
||||||
|
inactive-text="Text"
|
||||||
|
style="margin-left: 10px; width: 200px;"
|
||||||
|
:inactive-action-style="'backgroundColor: var(--sidebar)'"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<el-scrollbar height="350px">
|
||||||
|
<div
|
||||||
|
class="output-content"
|
||||||
|
contenteditable="false"
|
||||||
|
>
|
||||||
|
<template v-if="!showRawJson">
|
||||||
|
<span v-for="(message, index) of tabStorage.lastPromptGetResponse?.messages || []" :key="index">
|
||||||
|
{{ message.content.text }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ formattedJson }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, defineProps, computed, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { tabs } from '../panel';
|
||||||
|
import { PromptStorage } from './prompts';
|
||||||
|
|
||||||
|
defineComponent({ name: 'prompt-logger' });
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as PromptStorage;
|
||||||
|
|
||||||
|
const showRawJson = ref(false);
|
||||||
|
|
||||||
|
const formattedJson = computed(() => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(tabStorage.lastPromptGetResponse, null, 2);
|
||||||
|
} catch {
|
||||||
|
return 'Invalid JSON';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.prompt-logger {
|
||||||
|
border-radius: .5em;
|
||||||
|
background-color: var(--background);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-logger .el-switch__core {
|
||||||
|
border: 1px solid var(--main-color) !important;
|
||||||
|
width: 60px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-logger .el-switch .el-switch__action {
|
||||||
|
background-color: var(--main-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-logger .el-switch.is-checked .el-switch__action {
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-logger > span:first-child {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-logger .output-content {
|
||||||
|
border-radius: .5em;
|
||||||
|
padding: 15px;
|
||||||
|
min-height: 300px;
|
||||||
|
height: fit-content;
|
||||||
|
font-family: var(--code-font-family);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
user-select: text;
|
||||||
|
cursor: text;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.5;
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
}
|
||||||
|
</style>
|
130
app/src/components/main-panel/prompt/prompt-reader.vue
Normal file
130
app/src/components/main-panel/prompt/prompt-reader.vue
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h3>{{ currentPrompt.name }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="prompt-reader-container">
|
||||||
|
<el-form :model="formData" :rules="formRules" ref="formRef" label-position="top">
|
||||||
|
<el-form-item v-for="param in currentPrompt?.params" :key="param.name"
|
||||||
|
:label="param.name" :prop="param.name">
|
||||||
|
<el-input v-if="param.type === 'string'" v-model="formData[param.name]"
|
||||||
|
:placeholder="param.placeholder || `请输入${param.name}`" />
|
||||||
|
|
||||||
|
<el-input-number v-else-if="param.type === 'number'" v-model="formData[param.name]"
|
||||||
|
:placeholder="param.placeholder || `请输入${param.name}`" />
|
||||||
|
|
||||||
|
<el-switch v-else-if="param.type === 'boolean'" v-model="formData[param.name]" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="loading" @click="handleSubmit">
|
||||||
|
{{ t('read-prompt') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="resetForm">
|
||||||
|
{{ t('reset') }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, defineProps, watch, ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus';
|
||||||
|
import { tabs } from '../panel';
|
||||||
|
import { parsePromptTemplate, promptsManager, PromptStorage } from './prompts';
|
||||||
|
import { CasualRestAPI, PromptsGetResponse } from '@/hook/type';
|
||||||
|
import { useMessageBridge } from '@/api/message-bridge';
|
||||||
|
|
||||||
|
defineComponent({ name: 'prompt-reader' });
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as PromptStorage;
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
const formData = ref<Record<string, any>>({});
|
||||||
|
const loading = ref(false);
|
||||||
|
const responseData = ref<PromptsGetResponse>();
|
||||||
|
|
||||||
|
const currentPrompt = computed(() => {
|
||||||
|
const template = promptsManager.templates.find(template => template.name === tabStorage.currentPromptName);
|
||||||
|
const name = template?.name || '';
|
||||||
|
const params = template?.arguments || [];
|
||||||
|
|
||||||
|
const viewParams = params.map(param => ({
|
||||||
|
name: param.name,
|
||||||
|
type: 'string',
|
||||||
|
placeholder: t('enter') +' ' + param.name,
|
||||||
|
required: param.required
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
params: viewParams
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRules = computed<FormRules>(() => {
|
||||||
|
const rules: FormRules = {}
|
||||||
|
currentPrompt.value?.params.forEach(param => {
|
||||||
|
rules[param.name] = [
|
||||||
|
{
|
||||||
|
message: `${param.name} 是必填字段`,
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
});
|
||||||
|
|
||||||
|
const initFormData = () => {
|
||||||
|
formData.value = {}
|
||||||
|
currentPrompt.value?.params.forEach(param => {
|
||||||
|
formData.value[param.name] = param.type === 'number' ? 0 :
|
||||||
|
param.type === 'boolean' ? false : ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
formRef.value?.resetFields();
|
||||||
|
responseData.value = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
const bridge = useMessageBridge();
|
||||||
|
|
||||||
|
bridge.addCommandListener('prompts/get', (data: CasualRestAPI<PromptsGetResponse>) => {
|
||||||
|
tabStorage.lastPromptGetResponse = data.msg;
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
bridge.postMessage({
|
||||||
|
command: 'prompts/get',
|
||||||
|
data: { promptId: currentPrompt.value.name, args: formData.value }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => tabStorage.currentPromptName, () => {
|
||||||
|
initFormData();
|
||||||
|
resetForm();
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.prompt-reader-container {
|
||||||
|
background-color: var(--background);
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: .5em;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
155
app/src/components/main-panel/prompt/prompt-templates.vue
Normal file
155
app/src/components/main-panel/prompt/prompt-templates.vue
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div class="prompt-template-container-scrollbar">
|
||||||
|
<el-scrollbar height="500px">
|
||||||
|
<div class="prompt-template-container">
|
||||||
|
<div
|
||||||
|
class="item"
|
||||||
|
:class="{ 'active': tabStorage.currentPromptName === template.name }"
|
||||||
|
v-for="template of promptsManager.templates"
|
||||||
|
:key="template.name"
|
||||||
|
@click="handleClick(template)"
|
||||||
|
>
|
||||||
|
<span>{{ template.name }}</span>
|
||||||
|
<span>{{ template.description || '' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<div class="prompt-template-function-container">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="reloadPrompts({ first: false })"
|
||||||
|
>
|
||||||
|
{{ t('refresh') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMessageBridge } from '@/api/message-bridge';
|
||||||
|
import { CasualRestAPI, PromptTemplate, PromptsListResponse } from '@/hook/type';
|
||||||
|
import { onMounted, onUnmounted, defineProps } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { promptsManager, PromptStorage } from './prompts';
|
||||||
|
import { tabs } from '../panel';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const bridge = useMessageBridge();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as PromptStorage;
|
||||||
|
|
||||||
|
function reloadPrompts(option: { first: boolean }) {
|
||||||
|
bridge.postMessage({
|
||||||
|
command: 'prompts/list'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!option.first) {
|
||||||
|
ElMessage({
|
||||||
|
message: t('finish-refresh'),
|
||||||
|
type: 'success',
|
||||||
|
duration: 3000,
|
||||||
|
showClose: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick(template: PromptTemplate) {
|
||||||
|
tabStorage.currentPromptName = template.name;
|
||||||
|
tabStorage.lastPromptGetResponse = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let commandCancel: (() => void);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
commandCancel = bridge.addCommandListener('prompts/list', (data: CasualRestAPI<PromptsListResponse>) => {
|
||||||
|
promptsManager.templates = data.msg.prompts || [];
|
||||||
|
|
||||||
|
if (promptsManager.templates.length > 0) {
|
||||||
|
tabStorage.currentPromptName = promptsManager.templates[0].name;
|
||||||
|
tabStorage.lastPromptGetResponse = undefined;
|
||||||
|
}
|
||||||
|
}, { once: false });
|
||||||
|
|
||||||
|
reloadPrompts({ first: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (commandCancel){
|
||||||
|
commandCancel();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.prompt-template-container-scrollbar {
|
||||||
|
background-color: var(--background);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-template-container {
|
||||||
|
height: fit-content;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-template-function-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-template-function-container button {
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-template-container > .item {
|
||||||
|
margin: 3px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: .3em;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-template-container > .item:hover {
|
||||||
|
background-color: var(--main-light-color);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-template-container > .item.active {
|
||||||
|
background-color: var(--main-light-color);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-template-container > .item > span:first-child {
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-template-container > .item > span:last-child {
|
||||||
|
opacity: 0.6;
|
||||||
|
font-size: 12.5px;
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
51
app/src/components/main-panel/prompt/prompts.ts
Normal file
51
app/src/components/main-panel/prompt/prompts.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { PromptsGetResponse, PromptTemplate } from '@/hook/type';
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
|
export const promptsManager = reactive<{
|
||||||
|
current: PromptTemplate | undefined
|
||||||
|
templates: PromptTemplate[]
|
||||||
|
}>({
|
||||||
|
current: undefined,
|
||||||
|
templates: []
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface PromptStorage {
|
||||||
|
currentPromptName: string;
|
||||||
|
lastPromptGetResponse?: PromptsGetResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parsePromptTemplate(template: string): {
|
||||||
|
params: string[],
|
||||||
|
fill: (params: Record<string, string>) => string
|
||||||
|
} {
|
||||||
|
const paramRegex = /\{([^}]+)\}/g;
|
||||||
|
const params = new Set<string>();
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = paramRegex.exec(template)) !== null) {
|
||||||
|
params.add(match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paramList = Array.from(params);
|
||||||
|
|
||||||
|
const fill = (values: Record<string, string>): string => {
|
||||||
|
let result = template;
|
||||||
|
|
||||||
|
for (const param of paramList) {
|
||||||
|
if (values[param] === undefined) {
|
||||||
|
throw new Error(`缺少必要参数: ${param}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const param of paramList) {
|
||||||
|
result = result.replace(new RegExp(`\\{${param}\\}`, 'g'), values[param]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
params: paramList,
|
||||||
|
fill
|
||||||
|
};
|
||||||
|
}
|
@ -46,11 +46,12 @@ const props = defineProps({
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-module .left {
|
.resource-module .left {
|
||||||
width: 45%;
|
width: 45%;
|
||||||
|
max-width: 410px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-module .right {
|
.resource-module .right {
|
||||||
|
@ -1,25 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="resource-logger">
|
<div class="resource-logger">
|
||||||
<span>{{ "Response" }}</span>
|
<span>
|
||||||
<el-scrollbar height="300px">
|
<span>{{ t('response') }}</span>
|
||||||
<div
|
<span style="width: 200px;">
|
||||||
class="output-content"
|
<el-switch
|
||||||
contenteditable="false"
|
v-model="showRawJson"
|
||||||
>
|
inline-prompt
|
||||||
<span v-for="(content, index) of tabStorage.lastResourceReadResponse?.contents || []" :key="index">
|
active-text="JSON"
|
||||||
{{ content.text }}
|
inactive-text="Text"
|
||||||
</span>
|
style="margin-left: 10px; width: 200px;"
|
||||||
</div>
|
:inactive-action-style="'backgroundColor: var(--sidebar)'"
|
||||||
</el-scrollbar>
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<el-scrollbar height="350px">
|
||||||
|
<div
|
||||||
|
class="output-content"
|
||||||
|
contenteditable="false"
|
||||||
|
>
|
||||||
|
<template v-if="!showRawJson">
|
||||||
|
<span v-for="(content, index) of tabStorage.lastResourceReadResponse?.contents || []" :key="index">
|
||||||
|
{{ content.text }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ formattedJson }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, defineProps } from 'vue';
|
import { defineComponent, defineProps, computed, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { tabs } from '../panel';
|
import { tabs } from '../panel';
|
||||||
import { ResourceStorage } from './resources';
|
import { ResourceStorage } from './resources';
|
||||||
|
|
||||||
defineComponent({ name: 'resource-logger' });
|
defineComponent({ name: 'resource-logger' });
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabId: {
|
tabId: {
|
||||||
@ -31,31 +50,55 @@ const props = defineProps({
|
|||||||
const tab = tabs.content[props.tabId];
|
const tab = tabs.content[props.tabId];
|
||||||
const tabStorage = tab.storage as ResourceStorage;
|
const tabStorage = tab.storage as ResourceStorage;
|
||||||
|
|
||||||
|
const showRawJson = ref(false);
|
||||||
|
|
||||||
|
const formattedJson = computed(() => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(tabStorage.lastResourceReadResponse, null, 2);
|
||||||
|
} catch {
|
||||||
|
return 'Invalid JSON';
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
.resource-logger {
|
.resource-logger {
|
||||||
border-radius: .5em;
|
border-radius: .5em;
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-logger .output-content {
|
.resource-logger .el-switch__core {
|
||||||
border-radius: .5em;
|
border: 1px solid var(--main-color) !important;
|
||||||
padding: 15px;
|
width: 60px !important;
|
||||||
min-height: 300px;
|
|
||||||
height: fit-content;
|
|
||||||
font-family: var(--code-font-family);
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
user-select: text;
|
|
||||||
cursor: text;
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 1.5;
|
|
||||||
background-color: var(--sidebar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.resource-logger .el-switch .el-switch__action {
|
||||||
|
background-color: var(--main-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-logger .el-switch.is-checked .el-switch__action {
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-logger > span:first-child {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-logger .output-content {
|
||||||
|
border-radius: .5em;
|
||||||
|
padding: 15px;
|
||||||
|
min-height: 300px;
|
||||||
|
height: fit-content;
|
||||||
|
font-family: var(--code-font-family);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
user-select: text;
|
||||||
|
cursor: text;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.5;
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -15,16 +15,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="resource-template-function-container">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="reloadResources({ first: false })"
|
||||||
|
>
|
||||||
|
{{ t('refresh') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useMessageBridge } from '@/api/message-bridge';
|
import { useMessageBridge } from '@/api/message-bridge';
|
||||||
import { CasualRestAPI, ResourceTemplate, ResourceTemplatesListResponse } from '@/hook/type';
|
import { CasualRestAPI, ResourceTemplate, ResourceTemplatesListResponse } from '@/hook/type';
|
||||||
import { onMounted, onUnmounted, defineProps } from 'vue';
|
import { onMounted, onUnmounted, defineProps } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { resourcesManager, ResourceStorage } from './resources';
|
import { resourcesManager, ResourceStorage } from './resources';
|
||||||
import { tabs } from '../panel';
|
import { tabs } from '../panel';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
const bridge = useMessageBridge();
|
const bridge = useMessageBridge();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabId: {
|
tabId: {
|
||||||
@ -36,10 +47,19 @@ const props = defineProps({
|
|||||||
const tab = tabs.content[props.tabId];
|
const tab = tabs.content[props.tabId];
|
||||||
const tabStorage = tab.storage as ResourceStorage;
|
const tabStorage = tab.storage as ResourceStorage;
|
||||||
|
|
||||||
function reloadResources() {
|
function reloadResources(option: { first: boolean }) {
|
||||||
bridge.postMessage({
|
bridge.postMessage({
|
||||||
command: 'resources/templates/list'
|
command: 'resources/templates/list'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!option.first) {
|
||||||
|
ElMessage({
|
||||||
|
message: t('finish-refresh'),
|
||||||
|
type: 'success',
|
||||||
|
duration: 3000,
|
||||||
|
showClose: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClick(template: ResourceTemplate) {
|
function handleClick(template: ResourceTemplate) {
|
||||||
@ -48,8 +68,10 @@ function handleClick(template: ResourceTemplate) {
|
|||||||
tabStorage.lastResourceReadResponse = undefined;
|
tabStorage.lastResourceReadResponse = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
let commandCancel: (() => void);
|
||||||
bridge.addCommandListener('resources/templates/list', (data: CasualRestAPI<ResourceTemplatesListResponse>) => {
|
|
||||||
|
onMounted(() => {
|
||||||
|
commandCancel = bridge.addCommandListener('resources/templates/list', (data: CasualRestAPI<ResourceTemplatesListResponse>) => {
|
||||||
resourcesManager.templates = data.msg.resourceTemplates || [];
|
resourcesManager.templates = data.msg.resourceTemplates || [];
|
||||||
|
|
||||||
if (resourcesManager.templates.length > 0) {
|
if (resourcesManager.templates.length > 0) {
|
||||||
@ -57,17 +79,23 @@ onMounted(() => {
|
|||||||
// TODO: 恢复这部分响应?
|
// TODO: 恢复这部分响应?
|
||||||
tabStorage.lastResourceReadResponse = undefined;
|
tabStorage.lastResourceReadResponse = undefined;
|
||||||
}
|
}
|
||||||
}, { once: true });
|
}, { once: false });
|
||||||
|
|
||||||
reloadResources();
|
reloadResources({ first: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (commandCancel){
|
||||||
|
commandCancel();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.resource-template-container-scrollbar {
|
.resource-template-container-scrollbar {
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
|
margin-bottom: 10px;
|
||||||
border-radius: .5em;
|
border-radius: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +106,17 @@ onMounted(() => {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.resource-template-function-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-template-function-container button {
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
|
||||||
.resource-template-container > .item {
|
.resource-template-container > .item {
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
@ -69,6 +69,7 @@ function toggleConnectionPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.connected-status-container {
|
.connected-status-container {
|
||||||
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -41,12 +41,14 @@ export interface ToolsListResponse {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PromptTemplate {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
arguments: Argument[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface PromptsListResponse {
|
export interface PromptsListResponse {
|
||||||
prompts: Array<{
|
prompts: PromptTemplate[];
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
arguments: Argument[];
|
|
||||||
}>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResourceTemplate {
|
export interface ResourceTemplate {
|
||||||
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "إعادة تعيين",
|
"reset": "إعادة تعيين",
|
||||||
"read-resource": "قراءة الموارد",
|
"read-resource": "قراءة الموارد",
|
||||||
"enter": "إدخال",
|
"enter": "إدخال",
|
||||||
"connect.appearance.connect": "اتصال",
|
"refresh": "تحديث",
|
||||||
"connect.appearance.reconnect": "إعادة الاتصال"
|
"finish-refresh": "تم التحديث",
|
||||||
|
"response": "الاستجابة",
|
||||||
|
"read-prompt": "استخراج الكلمات الرئيسية"
|
||||||
}
|
}
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "Zurücksetzen",
|
"reset": "Zurücksetzen",
|
||||||
"read-resource": "Ressourcen lesen",
|
"read-resource": "Ressourcen lesen",
|
||||||
"enter": "Eingabe",
|
"enter": "Eingabe",
|
||||||
"connect.appearance.connect": "Verbinden",
|
"refresh": "Aktualisieren",
|
||||||
"connect.appearance.reconnect": "Wiederverbinden"
|
"finish-refresh": "Aktualisierung abgeschlossen",
|
||||||
|
"response": "Antwort",
|
||||||
|
"read-prompt": "Stichwörter extrahieren"
|
||||||
}
|
}
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "Reset",
|
"reset": "Reset",
|
||||||
"read-resource": "Read resources",
|
"read-resource": "Read resources",
|
||||||
"enter": "Input",
|
"enter": "Input",
|
||||||
"connect.appearance.connect": "Connect",
|
"refresh": "Refresh",
|
||||||
"connect.appearance.reconnect": "Reconnect"
|
"finish-refresh": "Refresh completed",
|
||||||
|
"response": "Response",
|
||||||
|
"read-prompt": "Extract keywords"
|
||||||
}
|
}
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "Réinitialiser",
|
"reset": "Réinitialiser",
|
||||||
"read-resource": "Lire les ressources",
|
"read-resource": "Lire les ressources",
|
||||||
"enter": "Entrée",
|
"enter": "Entrée",
|
||||||
"connect.appearance.connect": "Se connecter",
|
"refresh": "Rafraîchir",
|
||||||
"connect.appearance.reconnect": "Reconnecter"
|
"finish-refresh": "Actualisation terminée",
|
||||||
|
"response": "Réponse",
|
||||||
|
"read-prompt": "Extraire des mots-clés"
|
||||||
}
|
}
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "リセット",
|
"reset": "リセット",
|
||||||
"read-resource": "リソースを読み込む",
|
"read-resource": "リソースを読み込む",
|
||||||
"enter": "入力",
|
"enter": "入力",
|
||||||
"connect.appearance.connect": "接続",
|
"refresh": "更新",
|
||||||
"connect.appearance.reconnect": "再接続"
|
"finish-refresh": "更新が完了しました",
|
||||||
|
"response": "応答",
|
||||||
|
"read-prompt": "キーワードを抽出"
|
||||||
}
|
}
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "재설정",
|
"reset": "재설정",
|
||||||
"read-resource": "리소스 읽기",
|
"read-resource": "리소스 읽기",
|
||||||
"enter": "입력",
|
"enter": "입력",
|
||||||
"connect.appearance.connect": "연결",
|
"refresh": "새로 고침",
|
||||||
"connect.appearance.reconnect": "재연결"
|
"finish-refresh": "새로 고침 완료",
|
||||||
|
"response": "응답",
|
||||||
|
"read-prompt": "키워드 추출"
|
||||||
}
|
}
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "Сброс",
|
"reset": "Сброс",
|
||||||
"read-resource": "Чтение ресурсов",
|
"read-resource": "Чтение ресурсов",
|
||||||
"enter": "Ввод",
|
"enter": "Ввод",
|
||||||
"connect.appearance.connect": "Подключиться",
|
"refresh": "Обновить",
|
||||||
"connect.appearance.reconnect": "Переподключиться"
|
"finish-refresh": "Обновление завершено",
|
||||||
|
"response": "Ответ",
|
||||||
|
"read-prompt": "Извлечь ключевые слова"
|
||||||
}
|
}
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "重置",
|
"reset": "重置",
|
||||||
"read-resource": "读取资源",
|
"read-resource": "读取资源",
|
||||||
"enter": "输入",
|
"enter": "输入",
|
||||||
"connect.appearance.connect":"连接",
|
"refresh": "刷新",
|
||||||
"connect.appearance.reconnect":"重新连接"
|
"finish-refresh": "刷新完成",
|
||||||
|
"response": "响应",
|
||||||
|
"read-prompt": "提取提词"
|
||||||
}
|
}
|
@ -109,6 +109,8 @@
|
|||||||
"reset": "重置",
|
"reset": "重置",
|
||||||
"read-resource": "讀取資源",
|
"read-resource": "讀取資源",
|
||||||
"enter": "輸入",
|
"enter": "輸入",
|
||||||
"connect.appearance.connect": "連接",
|
"refresh": "重新整理",
|
||||||
"connect.appearance.reconnect": "重新連接"
|
"finish-refresh": "刷新完成",
|
||||||
|
"response": "響應",
|
||||||
|
"read-prompt": "提取關鍵詞"
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user