859 lines
21 KiB
Vue
859 lines
21 KiB
Vue
<!--
|
||||
|
|
Copyright 2024-2026 ReplyPier
|
|||
|
|
|
|||
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|||
|
|
you may not use this file except in compliance with the License.
|
|||
|
|
You may obtain a copy of the License at
|
|||
|
|
|
|||
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
|
|
|||
|
|
Unless required by applicable law or agreed to in writing, software
|
|||
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|||
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
|
|
See the License for the specific language governing permissions and
|
|||
|
|
limitations under the License.
|
|||
|
|
-->
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, computed } from 'vue'
|
|||
|
|
import { useAgentEdit } from '@/composables/useAgentEdit'
|
|||
|
|
import KnowledgeGrid from '@/components/agent/KnowledgeGrid.vue'
|
|||
|
|
import { ExitCondition } from '@/types/workflow'
|
|||
|
|
import type { UserGroup } from '@/types/user'
|
|||
|
|
|
|||
|
|
interface FormData {
|
|||
|
|
system_prompt: string
|
|||
|
|
temperature: number
|
|||
|
|
exit_condition: string
|
|||
|
|
auto_transfer_enabled: boolean
|
|||
|
|
transfer_group_id: string
|
|||
|
|
ask_for_rating: boolean
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const props = defineProps<{
|
|||
|
|
modelValue: FormData
|
|||
|
|
validationErrors: Record<string, string>
|
|||
|
|
userGroups: UserGroup[]
|
|||
|
|
loadingGroups: boolean
|
|||
|
|
agentId: string
|
|||
|
|
organizationId: string
|
|||
|
|
collapsedSections: Record<string, boolean>
|
|||
|
|
}>()
|
|||
|
|
|
|||
|
|
const emit = defineEmits<{
|
|||
|
|
(e: 'update:modelValue', value: FormData): void
|
|||
|
|
(e: 'validateField', field: string): void
|
|||
|
|
(e: 'toggleSection', section: string): void
|
|||
|
|
}>()
|
|||
|
|
|
|||
|
|
// Local reactive copy of the form data
|
|||
|
|
const localFormData = computed({
|
|||
|
|
get: () => props.modelValue,
|
|||
|
|
set: (value) => emit('update:modelValue', value)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// AI generation state
|
|||
|
|
const showAIPrompt = ref(false)
|
|||
|
|
const aiPrompt = ref('')
|
|||
|
|
|
|||
|
|
// Initialize agent edit composable (using a mock agent object for AI generation)
|
|||
|
|
const mockAgent = {
|
|||
|
|
id: 'temp-agent',
|
|||
|
|
name: 'Workflow Node Agent',
|
|||
|
|
display_name: 'Workflow Node Agent',
|
|||
|
|
description: 'Temporary agent for AI generation',
|
|||
|
|
agent_type: 'general',
|
|||
|
|
instructions: '',
|
|||
|
|
transfer_to_human: false,
|
|||
|
|
ask_for_rating: false,
|
|||
|
|
organization_id: 'temp',
|
|||
|
|
created_by: 'temp',
|
|||
|
|
created_at: new Date(),
|
|||
|
|
updated_at: new Date(),
|
|||
|
|
is_active: true,
|
|||
|
|
enable_rate_limiting: false,
|
|||
|
|
overall_limit_per_ip: 0,
|
|||
|
|
requests_per_sec: 0,
|
|||
|
|
conversation_starters: [],
|
|||
|
|
knowledge_base_ids: [],
|
|||
|
|
canvas_data: {}
|
|||
|
|
} as any
|
|||
|
|
|
|||
|
|
const { generateInstructions, isLoading: aiLoading, error: aiError } = useAgentEdit(mockAgent)
|
|||
|
|
|
|||
|
|
// Handle field updates
|
|||
|
|
const updateField = (field: keyof FormData, value: any) => {
|
|||
|
|
const updated = { ...localFormData.value, [field]: value }
|
|||
|
|
emit('update:modelValue', updated)
|
|||
|
|
emit('validateField', field)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Handle AI generation for system prompt
|
|||
|
|
const handleGenerateWithAI = async () => {
|
|||
|
|
if (!aiPrompt.value.trim()) return
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const generatedInstructions = await generateInstructions(aiPrompt.value)
|
|||
|
|
if (generatedInstructions.length > 0) {
|
|||
|
|
// Join the generated instructions with newlines
|
|||
|
|
updateField('system_prompt', generatedInstructions.join('\n'))
|
|||
|
|
showAIPrompt.value = false
|
|||
|
|
aiPrompt.value = ''
|
|||
|
|
}
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Failed to generate instructions:', err)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const validateFieldOnChange = (field: string) => {
|
|||
|
|
emit('validateField', field)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const toggleSection = (section: string) => {
|
|||
|
|
emit('toggleSection', section)
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div class="llm-node-config">
|
|||
|
|
<div class="form-group">
|
|||
|
|
<div class="instructions-header">
|
|||
|
|
<label for="system-prompt">指令 *</label>
|
|||
|
|
<button
|
|||
|
|
class="ai-generate-button"
|
|||
|
|
@click="showAIPrompt = true"
|
|||
|
|
:disabled="aiLoading"
|
|||
|
|
type="button"
|
|||
|
|
>
|
|||
|
|
<span class="ai-icon">✨</span>
|
|||
|
|
AI 生成
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<textarea
|
|||
|
|
id="system-prompt"
|
|||
|
|
:value="localFormData.system_prompt"
|
|||
|
|
@input="updateField('system_prompt', ($event.target as HTMLInputElement).value)"
|
|||
|
|
class="form-textarea"
|
|||
|
|
:class="{ 'error': validationErrors.system_prompt }"
|
|||
|
|
placeholder="输入给 AI 的系统提示词"
|
|||
|
|
rows="4"
|
|||
|
|
required
|
|||
|
|
@blur="validateFieldOnChange('system_prompt')"
|
|||
|
|
></textarea>
|
|||
|
|
<div v-if="validationErrors.system_prompt" class="error-message">
|
|||
|
|
{{ validationErrors.system_prompt }}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="temperature">温度</label>
|
|||
|
|
<input
|
|||
|
|
id="temperature"
|
|||
|
|
:value="localFormData.temperature"
|
|||
|
|
@input="updateField('temperature', parseFloat(($event.target as HTMLInputElement).value))"
|
|||
|
|
type="number"
|
|||
|
|
class="form-input"
|
|||
|
|
:class="{ 'error': validationErrors.temperature }"
|
|||
|
|
min="0"
|
|||
|
|
max="2"
|
|||
|
|
step="0.1"
|
|||
|
|
placeholder="0.7"
|
|||
|
|
@blur="validateFieldOnChange('temperature')"
|
|||
|
|
/>
|
|||
|
|
<div v-if="validationErrors.temperature" class="error-message">
|
|||
|
|
{{ validationErrors.temperature }}
|
|||
|
|
</div>
|
|||
|
|
<p class="help-text">
|
|||
|
|
控制回复的随机性。数值越低(0.1)越聚焦,数值越高(1.5)越有创造性。
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="exit-condition">退出条件 *</label>
|
|||
|
|
<select
|
|||
|
|
id="exit-condition"
|
|||
|
|
:value="localFormData.exit_condition"
|
|||
|
|
@change="updateField('exit_condition', ($event.target as HTMLSelectElement).value)"
|
|||
|
|
class="form-select"
|
|||
|
|
:class="{ 'error': validationErrors.exit_condition }"
|
|||
|
|
required
|
|||
|
|
@blur="validateFieldOnChange('exit_condition')"
|
|||
|
|
>
|
|||
|
|
<option :value="ExitCondition.SINGLE_EXECUTION">单次执行</option>
|
|||
|
|
<option :value="ExitCondition.CONTINUOUS_EXECUTION">持续执行</option>
|
|||
|
|
</select>
|
|||
|
|
<div v-if="validationErrors.exit_condition" class="error-message">
|
|||
|
|
{{ validationErrors.exit_condition }}
|
|||
|
|
</div>
|
|||
|
|
<p class="help-text">
|
|||
|
|
<template v-if="localFormData.exit_condition === ExitCondition.SINGLE_EXECUTION">
|
|||
|
|
AI 回复一次后总是进入下一个节点(默认)。
|
|||
|
|
</template>
|
|||
|
|
<template v-else-if="localFormData.exit_condition === ExitCondition.CONTINUOUS_EXECUTION">
|
|||
|
|
停留在当前节点持续对话,直到请求结束会话。
|
|||
|
|
</template>
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Transfer to Human Setting - Only for Continuous Execution -->
|
|||
|
|
<div v-if="localFormData.exit_condition === ExitCondition.CONTINUOUS_EXECUTION" class="form-group">
|
|||
|
|
<label class="checkbox-group">
|
|||
|
|
<input
|
|||
|
|
:checked="localFormData.auto_transfer_enabled"
|
|||
|
|
@change="updateField('auto_transfer_enabled', ($event.target as HTMLInputElement).checked)"
|
|||
|
|
type="checkbox"
|
|||
|
|
class="form-checkbox"
|
|||
|
|
/>
|
|||
|
|
<span class="checkbox-label">启用自动转人工</span>
|
|||
|
|
</label>
|
|||
|
|
<p class="help-text">
|
|||
|
|
启用后,AI 判断需要人工协助时可自动将会话转接给人工客服,且不会结束会话。
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<!-- Group selection - Only when transfer is enabled -->
|
|||
|
|
<div v-if="localFormData.auto_transfer_enabled" class="transfer-groups">
|
|||
|
|
<label for="transfer-group">转接分组</label>
|
|||
|
|
<div v-if="!loadingGroups">
|
|||
|
|
<select
|
|||
|
|
id="transfer-group"
|
|||
|
|
:value="localFormData.transfer_group_id"
|
|||
|
|
@change="updateField('transfer_group_id', ($event.target as HTMLSelectElement).value)"
|
|||
|
|
class="form-select"
|
|||
|
|
>
|
|||
|
|
<option value="">选择分组</option>
|
|||
|
|
<option v-for="group in userGroups" :key="group.id" :value="group.id">
|
|||
|
|
{{ group.name }}
|
|||
|
|
</option>
|
|||
|
|
</select>
|
|||
|
|
<p v-if="userGroups.length === 0" class="help-text no-groups-message">
|
|||
|
|
暂无可用分组。
|
|||
|
|
<router-link to="/human-agents" class="create-group-link">
|
|||
|
|
创建分组
|
|||
|
|
</router-link>
|
|||
|
|
</p>
|
|||
|
|
<p v-else class="help-text">
|
|||
|
|
选择负责处理转接会话的分组。
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
<div v-else class="loading-groups">
|
|||
|
|
正在加载分组……
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Request Rating Setting - Only for Continuous Execution -->
|
|||
|
|
<div v-if="localFormData.exit_condition === ExitCondition.CONTINUOUS_EXECUTION" class="form-group">
|
|||
|
|
<label class="checkbox-group">
|
|||
|
|
<input
|
|||
|
|
:checked="localFormData.ask_for_rating"
|
|||
|
|
@change="updateField('ask_for_rating', ($event.target as HTMLInputElement).checked)"
|
|||
|
|
type="checkbox"
|
|||
|
|
class="form-checkbox"
|
|||
|
|
/>
|
|||
|
|
<span class="checkbox-label">会话结束后邀请评分</span>
|
|||
|
|
</label>
|
|||
|
|
<p class="help-text">
|
|||
|
|
启用后,会话结束时将邀请客户对体验进行评分。
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Knowledge Section -->
|
|||
|
|
<div class="form-group">
|
|||
|
|
<div class="knowledge-section">
|
|||
|
|
<div class="section-header knowledge-header" @click="toggleSection('knowledge')">
|
|||
|
|
<div class="section-title">
|
|||
|
|
<svg class="section-icon" :class="{ 'rotated': collapsedSections.knowledge }" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|||
|
|
<polyline points="6,9 12,15 18,9"></polyline>
|
|||
|
|
</svg>
|
|||
|
|
<span>知识来源</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="section-content knowledge-content" :class="{ 'collapsed': collapsedSections.knowledge }">
|
|||
|
|
<div class="knowledge-wrapper">
|
|||
|
|
<KnowledgeGrid
|
|||
|
|
v-if="!collapsedSections.knowledge"
|
|||
|
|
:agent-id="agentId"
|
|||
|
|
:organization-id="organizationId"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- AI Prompt Modal -->
|
|||
|
|
<div v-if="showAIPrompt" class="ai-prompt-modal">
|
|||
|
|
<div class="ai-prompt-content">
|
|||
|
|
<h5>用 AI 生成指令</h5>
|
|||
|
|
<textarea
|
|||
|
|
v-model="aiPrompt"
|
|||
|
|
placeholder="描述你希望这个 AI 节点做什么。例如:“为一个帮助查询订单的客服助手生成指令”"
|
|||
|
|
rows="4"
|
|||
|
|
class="ai-prompt-textarea"
|
|||
|
|
></textarea>
|
|||
|
|
<div v-if="aiError" class="error-message">{{ aiError }}</div>
|
|||
|
|
<div class="ai-prompt-actions">
|
|||
|
|
<button
|
|||
|
|
class="cancel-ai-button"
|
|||
|
|
@click="showAIPrompt = false"
|
|||
|
|
:disabled="aiLoading"
|
|||
|
|
type="button"
|
|||
|
|
>
|
|||
|
|
取消
|
|||
|
|
</button>
|
|||
|
|
<button
|
|||
|
|
class="generate-ai-button"
|
|||
|
|
@click="handleGenerateWithAI"
|
|||
|
|
:disabled="aiLoading || !aiPrompt.trim()"
|
|||
|
|
type="button"
|
|||
|
|
>
|
|||
|
|
{{ aiLoading ? '生成中……' : '生成' }}
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
/* Import shared form styles */
|
|||
|
|
.form-group {
|
|||
|
|
margin-bottom: var(--space-sm);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-group label {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--text-color);
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-input,
|
|||
|
|
.form-textarea,
|
|||
|
|
.form-select {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 8px;
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
background: var(--background-color);
|
|||
|
|
color: var(--text-color);
|
|||
|
|
font-size: 0.85rem;
|
|||
|
|
transition: border-color 0.2s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-input:focus,
|
|||
|
|
.form-textarea:focus,
|
|||
|
|
.form-select:focus {
|
|||
|
|
outline: none;
|
|||
|
|
border-color: var(--primary-color);
|
|||
|
|
box-shadow: 0 0 0 2px rgba(201, 242, 78, 0.15);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-input.error,
|
|||
|
|
.form-textarea.error,
|
|||
|
|
.form-select.error {
|
|||
|
|
border-color: var(--error-color);
|
|||
|
|
background-color: rgba(239, 68, 68, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-input.error:focus,
|
|||
|
|
.form-textarea.error:focus,
|
|||
|
|
.form-select.error:focus {
|
|||
|
|
border-color: var(--error-color);
|
|||
|
|
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.error-message {
|
|||
|
|
color: var(--error-color);
|
|||
|
|
font-size: 0.75rem;
|
|||
|
|
margin-top: 4px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.error-message::before {
|
|||
|
|
content: "⚠";
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-textarea {
|
|||
|
|
resize: vertical;
|
|||
|
|
min-height: 70px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.checkbox-group {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: flex-start;
|
|||
|
|
gap: 8px;
|
|||
|
|
margin: 8px 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.checkbox-group input[type="checkbox"] {
|
|||
|
|
margin-top: 2px;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.checkbox-label {
|
|||
|
|
font-size: 0.85rem;
|
|||
|
|
color: var(--text-color);
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.help-text {
|
|||
|
|
font-size: 0.75rem;
|
|||
|
|
color: var(--text-muted);
|
|||
|
|
margin-top: 4px;
|
|||
|
|
line-height: 1.3;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-checkbox {
|
|||
|
|
width: 16px;
|
|||
|
|
height: 16px;
|
|||
|
|
margin: 0;
|
|||
|
|
cursor: pointer;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Instructions Header Styles */
|
|||
|
|
.instructions-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ai-generate-button {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 4px;
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|||
|
|
color: white;
|
|||
|
|
border: none;
|
|||
|
|
border-radius: var(--radius-sm);
|
|||
|
|
font-size: 0.7rem;
|
|||
|
|
font-weight: 500;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ai-generate-button:hover:not(:disabled) {
|
|||
|
|
transform: translateY(-1px);
|
|||
|
|
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ai-generate-button:disabled {
|
|||
|
|
opacity: 0.6;
|
|||
|
|
cursor: not-allowed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ai-icon {
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* AI Prompt Modal Styles */
|
|||
|
|
.ai-prompt-modal {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background: rgba(0, 0, 0, 0.5);
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
z-index: 1000;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ai-prompt-content {
|
|||
|
|
background: var(--background-color);
|
|||
|
|
border-radius: var(--radius-lg);
|
|||
|
|
padding: var(--space-lg);
|
|||
|
|
width: 90%;
|
|||
|
|
max-width: 500px;
|
|||
|
|
box-shadow: var(--shadow-xl);
|
|||
|
|
position: relative;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-md);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ai-prompt-content h5 {
|
|||
|
|
margin: 0 0 var(--space-sm) 0;
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--text-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ai-prompt-textarea {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 10px;
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
background: var(--background-soft);
|
|||
|
|
color: var(--text-color);
|
|||
|
|
font-size: 0.85rem;
|
|||
|
|
resize: vertical;
|
|||
|
|
min-height: 100px;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ai-prompt-actions {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: flex-end;
|
|||
|
|
gap: var(--space-sm);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cancel-ai-button,
|
|||
|
|
.generate-ai-button {
|
|||
|
|
padding: 8px 16px;
|
|||
|
|
border: none;
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
font-weight: 500;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s ease;
|
|||
|
|
font-size: 0.85rem;
|
|||
|
|
min-width: 80px;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cancel-ai-button {
|
|||
|
|
background: var(--background-muted);
|
|||
|
|
color: var(--text-muted);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cancel-ai-button:hover {
|
|||
|
|
background: var(--background-alt);
|
|||
|
|
color: var(--text-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.generate-ai-button {
|
|||
|
|
background: var(--accent-solid);
|
|||
|
|
color: var(--on-accent-solid);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.generate-ai-button:hover {
|
|||
|
|
background: var(--primary-dark);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.generate-ai-button:disabled {
|
|||
|
|
opacity: 0.6;
|
|||
|
|
cursor: not-allowed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Transfer groups styling */
|
|||
|
|
.transfer-groups {
|
|||
|
|
margin-top: var(--space-md);
|
|||
|
|
padding: var(--space-md);
|
|||
|
|
background: var(--background-alt);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.transfer-groups label {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--text-color);
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.no-groups-message {
|
|||
|
|
color: var(--text-muted);
|
|||
|
|
font-size: 0.75rem;
|
|||
|
|
margin-top: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.create-group-link {
|
|||
|
|
color: var(--primary-color);
|
|||
|
|
text-decoration: none;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.create-group-link:hover {
|
|||
|
|
text-decoration: underline;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.loading-groups {
|
|||
|
|
text-align: center;
|
|||
|
|
padding: var(--space-sm);
|
|||
|
|
color: var(--text-muted);
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Knowledge Section Styles */
|
|||
|
|
.knowledge-section {
|
|||
|
|
margin-top: var(--space-md);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
background: var(--background-color);
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-header .section-header {
|
|||
|
|
background: var(--background-muted);
|
|||
|
|
border-bottom: 1px solid var(--border-color);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
padding: var(--space-sm) var(--space-md);
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-header .section-header:hover {
|
|||
|
|
background: var(--background-alt);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.section-title {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: var(--space-xs);
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--text-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.section-icon {
|
|||
|
|
width: 16px;
|
|||
|
|
height: 16px;
|
|||
|
|
color: var(--text-muted);
|
|||
|
|
transition: transform 0.2s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.section-icon.rotated {
|
|||
|
|
transform: rotate(-90deg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content {
|
|||
|
|
padding: 0;
|
|||
|
|
max-height: none;
|
|||
|
|
overflow: visible;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content.collapsed {
|
|||
|
|
max-height: 0;
|
|||
|
|
padding-top: 0;
|
|||
|
|
padding-bottom: 0;
|
|||
|
|
opacity: 0;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-wrapper {
|
|||
|
|
position: relative;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Override KnowledgeGrid styles within LLMNodeConfig */
|
|||
|
|
.knowledge-content :deep(.knowledge-grid-container) {
|
|||
|
|
padding: var(--space-sm);
|
|||
|
|
background: transparent;
|
|||
|
|
border-top: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.knowledge-header) {
|
|||
|
|
margin-bottom: var(--space-sm);
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-xs);
|
|||
|
|
align-items: stretch;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.header-left) {
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-xs);
|
|||
|
|
align-items: stretch;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.header-left h3) {
|
|||
|
|
font-size: 1rem;
|
|||
|
|
margin-bottom: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.header-actions) {
|
|||
|
|
display: flex;
|
|||
|
|
gap: var(--space-xs);
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.action-button) {
|
|||
|
|
font-size: 0.7rem;
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
flex: 1;
|
|||
|
|
text-align: center;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.knowledge-grid) {
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.knowledge-grid-header),
|
|||
|
|
.knowledge-content :deep(.knowledge-grid-row) {
|
|||
|
|
grid-template-columns: 1fr 60px;
|
|||
|
|
min-height: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.header-cell),
|
|||
|
|
.knowledge-content :deep(.grid-cell) {
|
|||
|
|
padding: var(--space-xs) var(--space-sm);
|
|||
|
|
font-size: 0.75rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.header-cell:nth-child(2)),
|
|||
|
|
.knowledge-content :deep(.header-cell:nth-child(3)),
|
|||
|
|
.knowledge-content :deep(.header-cell:nth-child(4)),
|
|||
|
|
.knowledge-content :deep(.grid-cell:nth-child(2)),
|
|||
|
|
.knowledge-content :deep(.grid-cell:nth-child(3)),
|
|||
|
|
.knowledge-content :deep(.grid-cell:nth-child(4)) {
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.actions-cell) {
|
|||
|
|
width: 60px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.delete-button) {
|
|||
|
|
padding: 2px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.delete-icon) {
|
|||
|
|
width: 16px;
|
|||
|
|
height: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.knowledge-empty) {
|
|||
|
|
padding: var(--space-md);
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.warning-message) {
|
|||
|
|
font-size: 0.8rem;
|
|||
|
|
margin-bottom: var(--space-xs);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.warning-description) {
|
|||
|
|
font-size: 0.75rem;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.modal-content) {
|
|||
|
|
max-width: 30vw;
|
|||
|
|
max-height: 75vh;
|
|||
|
|
width: 65%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Ensure knowledge modals have proper z-index */
|
|||
|
|
.knowledge-content :deep(.modal-overlay) {
|
|||
|
|
z-index: 1001 !important;
|
|||
|
|
position: fixed !important;
|
|||
|
|
isolation: isolate;
|
|||
|
|
background: rgba(0, 0, 0, 0.4) !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.modal-overlay),
|
|||
|
|
.knowledge-content :deep(.modal-content) {
|
|||
|
|
pointer-events: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.modal-overlay) {
|
|||
|
|
backdrop-filter: blur(4px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.pagination) {
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: var(--space-xs);
|
|||
|
|
margin-top: var(--space-sm);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.pagination-button) {
|
|||
|
|
width: 100%;
|
|||
|
|
font-size: 0.7rem;
|
|||
|
|
padding: var(--space-xs);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.page-info) {
|
|||
|
|
font-size: 0.7rem;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Link modal overrides */
|
|||
|
|
.knowledge-content :deep(.link-modal) {
|
|||
|
|
max-width: 65vw;
|
|||
|
|
width: 60%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid .knowledge-grid-header),
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid .knowledge-grid-row) {
|
|||
|
|
grid-template-columns: 2fr 0fr 100px;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid .type-cell) {
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.source-cell) {
|
|||
|
|
padding-left: var(--space-sm);
|
|||
|
|
white-space: normal;
|
|||
|
|
word-break: break-word;
|
|||
|
|
font-size: 0.75rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.action-cell) {
|
|||
|
|
padding-right: var(--space-sm);
|
|||
|
|
text-align: right;
|
|||
|
|
width: 100px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.link-button),
|
|||
|
|
.knowledge-content :deep(.unlink-button) {
|
|||
|
|
min-width: 70px;
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
font-size: 0.7rem;
|
|||
|
|
display: inline-block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid) .action-cell {
|
|||
|
|
display: block !important;
|
|||
|
|
visibility: visible !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid) .link-button,
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid) .unlink-button {
|
|||
|
|
display: inline-block !important;
|
|||
|
|
visibility: visible !important;
|
|||
|
|
opacity: 1 !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 768px) {
|
|||
|
|
.knowledge-content :deep(.knowledge-grid-container) {
|
|||
|
|
padding: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.header-actions) {
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.action-button) {
|
|||
|
|
font-size: 0.65rem;
|
|||
|
|
padding: 3px 6px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid .knowledge-grid-header),
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid .knowledge-grid-row) {
|
|||
|
|
grid-template-columns: 2fr 0fr 100px !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.knowledge-content :deep(.org-knowledge-grid) .action-cell {
|
|||
|
|
display: block !important;
|
|||
|
|
grid-column: 3 !important;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|