优化页面布局

This commit is contained in:
锦恢 2025-04-24 19:03:32 +08:00
parent 8887da8ba9
commit dd9c117df7
6 changed files with 319 additions and 0 deletions

View File

@ -0,0 +1,25 @@
<template>
<div class="message-role">Agent</div>
<div class="message-text">
<div v-if="message.content" v-html="markdownToHtml(props.message.content)"></div>
</div>
<MessageMeta :message="props.message" />
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
import { markdownToHtml } from '../markdown';
import MessageMeta from './message-meta.vue';
const props = defineProps({
message: {
type: Object,
required: true
}
});
</script>
<style>
</style>

View File

@ -0,0 +1,4 @@
import Assistant from "./assistant.vue";
import Toolcall from "./toolcall.vue";
import User from "./user.vue";
export { Assistant, Toolcall, User };

View File

@ -0,0 +1,85 @@
<template>
<div class="message-meta" @mouseenter="showTime = true" @mouseleave="showTime = false">
<span v-if="usageStatistic" class="message-usage">
<span>
{{ t('input-token') }} {{ usageStatistic.input }}
</span>
<span>
{{ t('output-token') }} {{ usageStatistic.output }}
</span>
<span>
{{ t('total') }} {{ usageStatistic.total }}
</span>
<span>
{{ t('cache-hit-ratio') }} {{ usageStatistic.cacheHitRatio }}%
</span>
</span>
<span v-else class="message-usage">
<span>{{ t('server-not-support-statistic') }}</span>
</span>
<span v-show="showTime" class="message-time">
{{ props.message.extraInfo.serverName }} {{ t('answer-at') }}
{{ new Date(message.extraInfo.created).toLocaleString() }}
</span>
</div>
</template>
<script setup lang="ts">
import { defineComponent, defineProps, ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { makeUsageStatistic } from '../usage';
defineComponent({ name: 'message-meta' });
const { t } = useI18n();
const props = defineProps({
message: {
type: Object,
required: true
}
});
const usageStatistic = computed(() => {
return makeUsageStatistic(props.message.extraInfo);
});
console.log(props.message);
console.log(usageStatistic);
const showTime = ref(false);
</script>
<style scoped>
.message-meta {
margin-top: 8px;
font-size: 0.8em;
color: var(--el-text-color-secondary);
display: flex;
}
.message-time {
opacity: 0.7;
padding: 2px 6px 2px 0;
transition: opacity 0.3s ease;
}
.message-usage {
display: flex;
align-items: center;
}
.message-usage > span {
background-color: var(--el-fill-color-light);
padding: 2px 6px;
border-radius: 4px;
margin-right: 3px;
}
</style>

View File

@ -0,0 +1,156 @@
<template>
<div class="message-role">
Agent
<span class="message-reminder" v-if="!props.message.toolResult">
正在使用工具
<span class="tool-loading iconfont icon-double-loading">
</span>
</span>
</div>
<div class="message-text tool_calls">
<div v-if="props.message.content" v-html="markdownToHtml(props.message.content)"></div>
<div class="tool-calls">
<div v-for="(call, index) in props.message.tool_calls" :key="index" class="tool-call-item">
<div class="tool-call-header">
<span class="tool-type">{{ 'tool' }}</span>
<span class="tool-name">{{ call.function.name }}</span>
<el-button size="small" @click="createTest(call)">
<span class="iconfont icon-send"></span>
</el-button>
</div>
<div class="tool-arguments">
<div class="inner">
<div v-html="jsonResultToHtml(call.function.arguments)"></div>
</div>
</div>
</div>
</div>
<!-- 工具调用结果 -->
<div v-if="props.message.toolResult">
<div class="tool-call-header">
<span class="tool-name">{{ "响应" }}</span>
<span style="width: 200px;" class="tools-dialog-container">
<el-switch v-model="props.message.showJson!.value" inline-prompt active-text="JSON" inactive-text="Text"
style="margin-left: 10px; width: 200px;"
:inactive-action-style="'backgroundColor: var(--sidebar)'" />
</span>
</div>
<div class="tool-result" v-if="isValidJSON(props.message.toolResult)">
<div v-if="props.message.showJson!.value" class="tool-result-content">
<div class="inner">
<div v-html="jsonResultToHtml(props.message.toolResult)"></div>
</div>
</div>
<span v-else>
<div v-for="(item, index) in JSON.parse(props.message.toolResult)" :key="index">
<el-scrollbar width="100%">
<div v-if="item.type === 'text'" class="tool-text">{{ item.text }}</div>
<div v-else class="tool-other">{{ JSON.stringify(item) }}</div>
</el-scrollbar>
</div>
</span>
</div>
</div>
</div>
<MessageMeta :message="message" />
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
import MessageMeta from './message-meta.vue';
import { markdownToHtml } from '../markdown';
import { createTest } from '@/views/setting/llm';
const props = defineProps({
message: {
type: Object,
required: true
}
});
const jsonResultToHtml = (jsonString: string) => {
const formattedJson = JSON.stringify(JSON.parse(jsonString), null, 2);
const html = markdownToHtml('```json\n' + formattedJson + '\n```');
return html;
};
const isValidJSON = (str: string) => {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
};
</script>
<style>
.tool-calls {
margin-top: 10px;
}
.tool-call-item {
margin-bottom: 10px;
}
.tool-call-header {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.tool-name {
font-weight: bold;
color: var(--el-color-primary);
margin-right: 8px;
margin-bottom: 0;
display: flex;
align-items: center;
height: 26px;
}
.tool-type {
font-size: 0.8em;
color: var(--el-text-color-secondary);
background-color: var(--el-fill-color-light);
padding: 2px 6px;
display: flex;
align-items: center;
border-radius: 4px;
margin-right: 10px;
}
.tool-arguments {
margin: 0;
padding: 8px;
background-color: var(--el-fill-color-light);
border-radius: 4px;
font-family: monospace;
font-size: 0.9em;
}
.tool-result {
padding: 8px;
background-color: var(--el-fill-color-light);
border-radius: 4px;
}
.tool-text {
white-space: pre-wrap;
line-height: 1.6;
}
.tool-other {
font-family: monospace;
font-size: 0.9em;
color: var(--el-text-color-secondary);
margin-top: 4px;
}
</style>

View File

@ -0,0 +1,22 @@
<template>
<div class="message-role"></div>
<div class="message-text">
<span>{{ props.message.content }}</span>
</div>
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
const props = defineProps({
message: {
type: Object,
required: true
}
});
</script>
<style>
</style>

View File

@ -0,0 +1,27 @@
import { PostMessageble } from "../adapter";
import { MCPClient } from "./connect";
export async function lookupEnvVarHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
try {
const { keys } = data;
const values = keys.map((key: string) => process.env[key] || '');
webview.postMessage({
command: 'lookup-env-var',
data: {
code: 200,
msg: values
}
});
} catch (error) {
webview.postMessage({
command: 'lookup-env-var',
data: {
code: 500,
msg: `Failed to lookup env vars: ${(error as Error).message}`
}
});
}
}