|
|
@@ -24,14 +24,139 @@
|
|
|
/>
|
|
|
<div class="input-actions">
|
|
|
<div class="action-left">
|
|
|
- <a-tooltip title="从本地上传">
|
|
|
- <div class="btn-upload" @click="emit('file-upload')">
|
|
|
- <div class="plus-icon"></div>
|
|
|
+ <div class="btn-upload-wrapper">
|
|
|
+ <a-tooltip title="添加上下文">
|
|
|
+ <div class="btn-upload" @click="togglePopup">
|
|
|
+ <div class="plus-icon"></div>
|
|
|
+ </div>
|
|
|
+ </a-tooltip>
|
|
|
+ <div v-if="showPopup" ref="popupRef" class="upload-popup" @click.stop>
|
|
|
+ <div class="popup-item" @click="handleAddAttachment">
|
|
|
+ <div class="popup-item-icon attach-icon"></div>
|
|
|
+ <span class="popup-item-text">添加附件</span>
|
|
|
+ </div>
|
|
|
+ <div class="popup-item" @click="handleInsertMention">
|
|
|
+ <div class="popup-item-icon mention-icon"></div>
|
|
|
+ <span class="popup-item-text">插入 @ 提及</span>
|
|
|
+ </div>
|
|
|
+ <div class="popup-item" @click="handleInsertSession">
|
|
|
+ <div class="popup-item-icon session-icon"></div>
|
|
|
+ <span class="popup-item-text">插入 # 会话</span>
|
|
|
+ </div>
|
|
|
+ <div class="popup-item" @click="handleInsertCommand">
|
|
|
+ <div class="popup-item-icon command-icon"></div>
|
|
|
+ <span class="popup-item-text">插入 / 命令</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </a-tooltip>
|
|
|
+ </div>
|
|
|
+ <div class="action-divider"></div>
|
|
|
+ <div class="btn-edit-mode-wrapper">
|
|
|
+ <a-tooltip title="切换模式">
|
|
|
+ <div class="btn-edit-mode" @click="toggleEditModePopup">
|
|
|
+ <div class="btn-icon" :class="`mode-icon-${selectedEditMode.key}`"></div>
|
|
|
+ <span class="btn-text">{{ selectedEditMode.label }}</span>
|
|
|
+ <div class="btn-arrow"></div>
|
|
|
+ </div>
|
|
|
+ </a-tooltip>
|
|
|
+ <div v-if="showEditModePopup" ref="editModePopupRef" class="edit-mode-popup">
|
|
|
+ <div
|
|
|
+ v-for="item in editModeOptions"
|
|
|
+ :key="item.key"
|
|
|
+ class="edit-mode-item"
|
|
|
+ :class="{ active: selectedEditMode.key === item.key }"
|
|
|
+ @click="handleSelectEditMode(item)"
|
|
|
+ >
|
|
|
+ <div class="edit-mode-item-icon" :class="`mode-icon-${item.key}`"></div>
|
|
|
+ <div class="edit-mode-item-main">
|
|
|
+ <span class="edit-mode-item-label">{{ item.label }}</span>
|
|
|
+ <span class="edit-mode-item-desc">{{ item.desc }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="edit-mode-check" :class="{ visible: selectedEditMode.key === item.key }"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<input ref="fileInputRef" type="file" style="display: none" @change="emit('file-change', $event)" accept=".pdf" />
|
|
|
</div>
|
|
|
<div class="action-right">
|
|
|
+ <div class="btn-context-wrapper" ref="contextPopupRef">
|
|
|
+ <div class="btn-context" @click="toggleContextPopup" @mouseenter="showContextPopup = true" @mouseleave="showContextPopup = false">
|
|
|
+ <svg class="context-ring" viewBox="0 0 32 32">
|
|
|
+ <circle class="context-ring-bg" cx="16" cy="16" r="13" />
|
|
|
+ <circle
|
|
|
+ class="context-ring-fill"
|
|
|
+ cx="16"
|
|
|
+ cy="16"
|
|
|
+ r="13"
|
|
|
+ :stroke-dasharray="contextCircumference"
|
|
|
+ :stroke-dashoffset="contextOffset"
|
|
|
+ :style="{ stroke: contextColor }"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ <span class="context-percent">{{ contextPercent }}%</span>
|
|
|
+ </div>
|
|
|
+ <div v-if="showContextPopup" class="context-popup" @click.stop>
|
|
|
+ <div class="context-popup-title">上下文容量</div>
|
|
|
+ <div class="context-popup-info">
|
|
|
+ <span class="context-used">{{ contextDisplayUsed }}</span>
|
|
|
+ <span class="context-sep">/</span>
|
|
|
+ <span class="context-max">{{ contextDisplayMax }}</span>
|
|
|
+ <span class="context-pct">({{ contextPercent }}%)</span>
|
|
|
+ </div>
|
|
|
+ <div class="context-popup-bar-track">
|
|
|
+ <div class="context-popup-bar-fill" :style="{ width: contextPercent + '%', background: contextColor }"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="action-divider"></div>
|
|
|
+ <div class="btn-model-wrapper" ref="modelPopupRef">
|
|
|
+ <a-tooltip title="选择模型">
|
|
|
+ <div class="btn-model" @click="toggleModelPopup">
|
|
|
+ <span class="btn-text">{{ selectedModel.label }}</span>
|
|
|
+ <div class="btn-arrow"></div>
|
|
|
+ </div>
|
|
|
+ </a-tooltip>
|
|
|
+ <div v-if="showModelPopup" class="model-popup" @click.stop>
|
|
|
+ <div
|
|
|
+ v-for="item in modelOptions"
|
|
|
+ :key="item.key"
|
|
|
+ class="model-popup-item"
|
|
|
+ :class="{ active: selectedModel.key === item.key }"
|
|
|
+ @click="handleSelectModel(item)"
|
|
|
+ >
|
|
|
+ <div class="model-item-main">
|
|
|
+ <span class="model-item-label">{{ item.label }}</span>
|
|
|
+ <span class="model-item-desc">{{ item.desc }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="model-item-check" :class="{ visible: selectedModel.key === item.key }"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="action-divider"></div>
|
|
|
+ <div class="btn-think-wrapper" ref="thinkPopupRef">
|
|
|
+ <a-tooltip title="思考级别">
|
|
|
+ <div class="btn-think" @click="toggleThinkPopup">
|
|
|
+ <div class="btn-icon think-icon"></div>
|
|
|
+ <span class="btn-text">{{ selectedThinkLevel.label }}</span>
|
|
|
+ <div class="btn-arrow"></div>
|
|
|
+ </div>
|
|
|
+ </a-tooltip>
|
|
|
+ <div v-if="showThinkPopup" class="think-popup" @click.stop>
|
|
|
+ <div
|
|
|
+ v-for="item in thinkLevelOptions"
|
|
|
+ :key="item.key"
|
|
|
+ class="think-popup-item"
|
|
|
+ :class="{ active: selectedThinkLevel.key === item.key }"
|
|
|
+ @click="handleSelectThinkLevel(item)"
|
|
|
+ >
|
|
|
+ <div class="think-item-main">
|
|
|
+ <span class="think-item-label">{{ item.label }}</span>
|
|
|
+ <span class="think-item-desc">{{ item.desc }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="think-item-check" :class="{ visible: selectedThinkLevel.key === item.key }"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="action-divider"></div>
|
|
|
<div class="btn-send" :class="{ loading, disabled: !modelValue.trim() }" @click="handleSend">
|
|
|
<div class="send-icon"></div>
|
|
|
</div>
|
|
|
@@ -41,16 +166,25 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
- import { ref } from 'vue';
|
|
|
+ import { ref, computed } from 'vue';
|
|
|
+ import { onClickOutside } from '@vueuse/core';
|
|
|
import { CloseOutlined } from '@ant-design/icons-vue';
|
|
|
import { formatFileSize } from './utils';
|
|
|
import type { AttachedFile } from './types';
|
|
|
|
|
|
- const props = defineProps<{
|
|
|
- modelValue: string;
|
|
|
- pendingFile: AttachedFile | null;
|
|
|
- loading: boolean;
|
|
|
- }>();
|
|
|
+ const props = withDefaults(
|
|
|
+ defineProps<{
|
|
|
+ modelValue: string;
|
|
|
+ pendingFile: AttachedFile | null;
|
|
|
+ loading: boolean;
|
|
|
+ contextUsed?: number;
|
|
|
+ contextMax?: number;
|
|
|
+ }>(),
|
|
|
+ {
|
|
|
+ contextUsed: 0,
|
|
|
+ contextMax: 200000,
|
|
|
+ }
|
|
|
+ );
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
(e: 'update:modelValue', value: string): void;
|
|
|
@@ -58,9 +192,172 @@
|
|
|
(e: 'file-upload'): void;
|
|
|
(e: 'file-change', event: Event): void;
|
|
|
(e: 'remove-pending-file'): void;
|
|
|
+ (e: 'model-change', modelKey: string): void;
|
|
|
+ (e: 'think-level-change', levelKey: string): void;
|
|
|
}>();
|
|
|
|
|
|
+ const handleSelectModel = (item: ModelOption) => {
|
|
|
+ selectedModel.value = item;
|
|
|
+ showModelPopup.value = false;
|
|
|
+ emit('model-change', item.key);
|
|
|
+ };
|
|
|
+
|
|
|
const fileInputRef = ref<HTMLInputElement>();
|
|
|
+ const popupRef = ref<HTMLElement>();
|
|
|
+ const showPopup = ref(false);
|
|
|
+
|
|
|
+ // 上下文容量
|
|
|
+ const contextPopupRef = ref<HTMLElement>();
|
|
|
+ const showContextPopup = ref(false);
|
|
|
+
|
|
|
+ const contextPercent = computed(() => {
|
|
|
+ if (!props.contextMax) return 0;
|
|
|
+ return Math.min(100, Math.round((props.contextUsed / props.contextMax) * 100));
|
|
|
+ });
|
|
|
+
|
|
|
+ const contextCircumference = computed(() => 2 * Math.PI * 13);
|
|
|
+ const contextOffset = computed(() => contextCircumference.value * (1 - contextPercent.value / 100));
|
|
|
+
|
|
|
+ const contextColor = computed(() => {
|
|
|
+ if (contextPercent.value >= 90) return '#ff4d4f';
|
|
|
+ if (contextPercent.value >= 70) return '#faad14';
|
|
|
+ return '#52c41a';
|
|
|
+ });
|
|
|
+
|
|
|
+ const contextDisplayUsed = computed(() => {
|
|
|
+ if (props.contextUsed >= 10000) return (props.contextUsed / 10000).toFixed(1) + '万';
|
|
|
+ return props.contextUsed.toLocaleString();
|
|
|
+ });
|
|
|
+
|
|
|
+ const contextDisplayMax = computed(() => {
|
|
|
+ if (props.contextMax >= 10000) return (props.contextMax / 10000).toFixed(0) + '万';
|
|
|
+ return props.contextMax.toLocaleString();
|
|
|
+ });
|
|
|
+
|
|
|
+ const toggleContextPopup = () => {
|
|
|
+ showContextPopup.value = !showContextPopup.value;
|
|
|
+ };
|
|
|
+
|
|
|
+ onClickOutside(contextPopupRef, () => {
|
|
|
+ showContextPopup.value = false;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 模型选择
|
|
|
+ const modelPopupRef = ref<HTMLElement>();
|
|
|
+ const showModelPopup = ref(false);
|
|
|
+
|
|
|
+ interface ModelOption {
|
|
|
+ key: string;
|
|
|
+ label: string;
|
|
|
+ desc: string;
|
|
|
+ }
|
|
|
+
|
|
|
+ const modelOptions: ModelOption[] = [
|
|
|
+ { key: 'deepseek-v4-pro', label: 'DeepSeek-V4-Pro', desc: '默认模型,综合能力强' },
|
|
|
+ { key: 'deepseek-v4', label: 'DeepSeek-V4', desc: '轻量快速,适合简单任务' },
|
|
|
+ { key: 'gpt-4o', label: 'GPT-4o', desc: 'OpenAI 多模态模型' },
|
|
|
+ { key: 'gpt-4o-mini', label: 'GPT-4o Mini', desc: 'OpenAI 轻量模型' },
|
|
|
+ { key: 'claude-sonnet-4', label: 'Claude Sonnet 4', desc: 'Anthropic 高性能模型' },
|
|
|
+ { key: 'qwen-max', label: '通义千问 Max', desc: '阿里云旗舰模型' },
|
|
|
+ ];
|
|
|
+
|
|
|
+ const selectedModel = ref<ModelOption>(modelOptions[0]);
|
|
|
+
|
|
|
+ const toggleModelPopup = () => {
|
|
|
+ showModelPopup.value = !showModelPopup.value;
|
|
|
+ };
|
|
|
+
|
|
|
+ onClickOutside(modelPopupRef, () => {
|
|
|
+ showModelPopup.value = false;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 思考级别
|
|
|
+ const thinkPopupRef = ref<HTMLElement>();
|
|
|
+ const showThinkPopup = ref(false);
|
|
|
+
|
|
|
+ interface ThinkLevelOption {
|
|
|
+ key: string;
|
|
|
+ label: string;
|
|
|
+ desc: string;
|
|
|
+ }
|
|
|
+
|
|
|
+ const thinkLevelOptions: ThinkLevelOption[] = [
|
|
|
+ { key: 'off', label: '关闭', desc: '不使用深度思考,响应最快' },
|
|
|
+ { key: 'high', label: '高', desc: '深度思考,平衡速度与质量' },
|
|
|
+ { key: 'highest', label: '最高', desc: '极致思考,质量最高但较慢' },
|
|
|
+ ];
|
|
|
+
|
|
|
+ const selectedThinkLevel = ref<ThinkLevelOption>(thinkLevelOptions[0]);
|
|
|
+
|
|
|
+ const toggleThinkPopup = () => {
|
|
|
+ showThinkPopup.value = !showThinkPopup.value;
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleSelectThinkLevel = (item: ThinkLevelOption) => {
|
|
|
+ selectedThinkLevel.value = item;
|
|
|
+ showThinkPopup.value = false;
|
|
|
+ emit('think-level-change', item.key);
|
|
|
+ };
|
|
|
+
|
|
|
+ onClickOutside(thinkPopupRef, () => {
|
|
|
+ showThinkPopup.value = false;
|
|
|
+ });
|
|
|
+
|
|
|
+ const editModePopupRef = ref<HTMLElement>();
|
|
|
+ const showEditModePopup = ref(false);
|
|
|
+
|
|
|
+ interface EditModeOption {
|
|
|
+ key: string;
|
|
|
+ label: string;
|
|
|
+ desc: string;
|
|
|
+ }
|
|
|
+
|
|
|
+ const editModeOptions: EditModeOption[] = [
|
|
|
+ { key: 'confirm', label: '变更前确认', desc: '改文件前先问我' },
|
|
|
+ { key: 'auto', label: '自动编辑', desc: '自动编辑文件' },
|
|
|
+ { key: 'plan', label: '计划模式', desc: '编辑前先出计划' },
|
|
|
+ { key: 'full', label: '完全访问', desc: '减少确认次数' },
|
|
|
+ ];
|
|
|
+
|
|
|
+ const selectedEditMode = ref<EditModeOption>(editModeOptions[1]);
|
|
|
+
|
|
|
+ onClickOutside(editModePopupRef, () => {
|
|
|
+ showEditModePopup.value = false;
|
|
|
+ });
|
|
|
+
|
|
|
+ const toggleEditModePopup = () => {
|
|
|
+ showEditModePopup.value = !showEditModePopup.value;
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleSelectEditMode = (item: EditModeOption) => {
|
|
|
+ selectedEditMode.value = item;
|
|
|
+ showEditModePopup.value = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ onClickOutside(popupRef, () => {
|
|
|
+ showPopup.value = false;
|
|
|
+ });
|
|
|
+
|
|
|
+ const togglePopup = () => {
|
|
|
+ showPopup.value = !showPopup.value;
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleAddAttachment = () => {
|
|
|
+ showPopup.value = false;
|
|
|
+ emit('file-upload');
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleInsertMention = () => {
|
|
|
+ showPopup.value = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleInsertSession = () => {
|
|
|
+ showPopup.value = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleInsertCommand = () => {
|
|
|
+ showPopup.value = false;
|
|
|
+ };
|
|
|
|
|
|
const handleEnterKey = (e: KeyboardEvent) => {
|
|
|
if (!e.shiftKey && props.modelValue.trim()) {
|
|
|
@@ -174,18 +471,64 @@
|
|
|
align-items: center;
|
|
|
margin-top: 12px;
|
|
|
|
|
|
+ .mode-icon-confirm {
|
|
|
+ background-image: var(--img-chat-mode-confirm-icon);
|
|
|
+ }
|
|
|
+ .mode-icon-auto {
|
|
|
+ background-image: var(--img-chat-mode-auto-icon);
|
|
|
+ }
|
|
|
+ .mode-icon-plan {
|
|
|
+ background-image: var(--img-chat-mode-plan-icon);
|
|
|
+ }
|
|
|
+ .mode-icon-full {
|
|
|
+ background-image: var(--img-chat-mode-full-icon);
|
|
|
+ }
|
|
|
+
|
|
|
.action-left,
|
|
|
.action-right {
|
|
|
display: flex;
|
|
|
gap: 8px;
|
|
|
+ .action-divider {
|
|
|
+ width: 1px;
|
|
|
+ height: 18px;
|
|
|
+ background: rgba(255, 255, 255, 0.15);
|
|
|
+ align-self: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+ .btn-icon {
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+ .think-icon {
|
|
|
+ background-image: var(--img-chat-thinking-icon);
|
|
|
+ }
|
|
|
+ .btn-text {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #e0e6ed;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ .btn-arrow {
|
|
|
+ width: 0;
|
|
|
+ height: 0;
|
|
|
+ border-left: 4px solid transparent;
|
|
|
+ border-right: 4px solid transparent;
|
|
|
+ border-top: 4px solid #8b949e;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+ .btn-upload-wrapper {
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
.btn-upload {
|
|
|
width: 30px;
|
|
|
height: 30px;
|
|
|
border-radius: 6px;
|
|
|
- margin-right: 10px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
|
.plus-icon {
|
|
|
width: 20px;
|
|
|
@@ -195,6 +538,443 @@
|
|
|
background-size: 100% 100%;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
+ .upload-popup {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 38px;
|
|
|
+ left: 0;
|
|
|
+ background: #0a2a3f;
|
|
|
+ border: 1px solid #1a4a6f;
|
|
|
+ border-radius: 8px;
|
|
|
+ min-width: 160px;
|
|
|
+ z-index: 1000;
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
|
+
|
|
|
+ .popup-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ padding: 9px 16px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background 0.2s;
|
|
|
+ border-radius: 8px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(10, 132, 255, 0.15);
|
|
|
+ }
|
|
|
+
|
|
|
+ .popup-item-icon {
|
|
|
+ width: 18px;
|
|
|
+ height: 18px;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .attach-icon {
|
|
|
+ background-image: var(--img-chat-popup-attach-icon);
|
|
|
+ }
|
|
|
+
|
|
|
+ .mention-icon {
|
|
|
+ background-image: var(--img-chat-popup-mention-icon);
|
|
|
+ }
|
|
|
+
|
|
|
+ .session-icon {
|
|
|
+ background-image: var(--img-chat-popup-session-icon);
|
|
|
+ }
|
|
|
+
|
|
|
+ .command-icon {
|
|
|
+ background-image: var(--img-chat-popup-command-icon);
|
|
|
+ }
|
|
|
+
|
|
|
+ .popup-item-text {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #e0e6ed;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .btn-edit-mode-wrapper {
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+ .btn-edit-mode {
|
|
|
+ height: 28px;
|
|
|
+ padding: 0 10px;
|
|
|
+ border-radius: 6px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.12);
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #3a8fd4;
|
|
|
+ background: rgba(10, 132, 255, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .edit-mode-popup {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 38px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ background: #0a2a3f;
|
|
|
+ border: 1px solid #1a4a6f;
|
|
|
+ border-radius: 8px;
|
|
|
+ min-width: 200px;
|
|
|
+ z-index: 1000;
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
|
+
|
|
|
+ .edit-mode-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 10px 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background 0.2s;
|
|
|
+ border-radius: 6px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(10, 132, 255, 0.15);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: rgba(10, 132, 255, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ .edit-mode-item-icon {
|
|
|
+ width: 18px;
|
|
|
+ height: 18px;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-right: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .edit-mode-item-main {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 2px;
|
|
|
+
|
|
|
+ .edit-mode-item-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #e0e6ed;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .edit-mode-item-desc {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #8b949e;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .edit-mode-check {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ visibility: hidden;
|
|
|
+
|
|
|
+ &.visible {
|
|
|
+ visibility: visible;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '✓';
|
|
|
+ font-size: 12px;
|
|
|
+ color: #3a8fd4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .btn-context-wrapper {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .btn-context {
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+ border-radius: 6px;
|
|
|
+ transition: background 0.2s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(10, 132, 255, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ .context-ring {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ transform: rotate(-90deg);
|
|
|
+
|
|
|
+ .context-ring-bg {
|
|
|
+ fill: none;
|
|
|
+ stroke: rgba(255, 255, 255, 0.1);
|
|
|
+ stroke-width: 3;
|
|
|
+ }
|
|
|
+
|
|
|
+ .context-ring-fill {
|
|
|
+ fill: none;
|
|
|
+ stroke-width: 3;
|
|
|
+ stroke-linecap: round;
|
|
|
+ transition: stroke-dashoffset 0.3s ease;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .context-percent {
|
|
|
+ position: absolute;
|
|
|
+ font-size: 8px;
|
|
|
+ color: #e0e6ed;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .context-popup {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 42px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ background: #0a2a3f;
|
|
|
+ border: 1px solid #1a4a6f;
|
|
|
+ border-radius: 8px;
|
|
|
+ min-width: 200px;
|
|
|
+ padding: 14px 16px;
|
|
|
+ z-index: 1000;
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
|
+
|
|
|
+ .context-popup-title {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #8b949e;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .context-popup-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 2px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ .context-used {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #e0e6ed;
|
|
|
+ }
|
|
|
+
|
|
|
+ .context-sep {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #8b949e;
|
|
|
+ }
|
|
|
+
|
|
|
+ .context-max {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #8b949e;
|
|
|
+ }
|
|
|
+
|
|
|
+ .context-pct {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #8b949e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .context-popup-bar-track {
|
|
|
+ width: 100%;
|
|
|
+ height: 6px;
|
|
|
+ background: rgba(255, 255, 255, 0.08);
|
|
|
+ border-radius: 3px;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .context-popup-bar-fill {
|
|
|
+ height: 100%;
|
|
|
+ border-radius: 3px;
|
|
|
+ transition: width 0.3s ease;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .btn-model-wrapper {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .btn-model {
|
|
|
+ height: 28px;
|
|
|
+ padding: 0 10px;
|
|
|
+ border-radius: 6px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.12);
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #3a8fd4;
|
|
|
+ background: rgba(10, 132, 255, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .model-popup {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 38px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ background: #0a2a3f;
|
|
|
+ border: 1px solid #1a4a6f;
|
|
|
+ border-radius: 8px;
|
|
|
+ min-width: 220px;
|
|
|
+ z-index: 1000;
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
|
+
|
|
|
+ .model-popup-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 10px 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background 0.2s;
|
|
|
+ border-radius: 6px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(10, 132, 255, 0.15);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: rgba(10, 132, 255, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ .model-item-main {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 2px;
|
|
|
+
|
|
|
+ .model-item-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #e0e6ed;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .model-item-desc {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #8b949e;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .model-item-check {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ visibility: hidden;
|
|
|
+
|
|
|
+ &.visible {
|
|
|
+ visibility: visible;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '✓';
|
|
|
+ font-size: 12px;
|
|
|
+ color: #3a8fd4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .btn-think-wrapper {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .btn-think {
|
|
|
+ height: 28px;
|
|
|
+ padding: 0 10px;
|
|
|
+ border-radius: 6px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.12);
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #3a8fd4;
|
|
|
+ background: rgba(10, 132, 255, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .think-popup {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 38px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ background: #0a2a3f;
|
|
|
+ border: 1px solid #1a4a6f;
|
|
|
+ border-radius: 8px;
|
|
|
+ min-width: 200px;
|
|
|
+ z-index: 1000;
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
|
+
|
|
|
+ .think-popup-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 10px 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background 0.2s;
|
|
|
+ border-radius: 6px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(10, 132, 255, 0.15);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: rgba(10, 132, 255, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ .think-item-main {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 2px;
|
|
|
+
|
|
|
+ .think-item-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #e0e6ed;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .think-item-desc {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #8b949e;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .think-item-check {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ visibility: hidden;
|
|
|
+
|
|
|
+ &.visible {
|
|
|
+ visibility: visible;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '✓';
|
|
|
+ font-size: 12px;
|
|
|
+ color: #3a8fd4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
.btn-send {
|
|
|
width: 32px;
|
|
|
height: 32px;
|