@szjy/ai-coms 0.1.8 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,162 @@
1
+ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
2
+ token: {
3
+ type: StringConstructor;
4
+ default: string;
5
+ };
6
+ identifier: {
7
+ type: StringConstructor;
8
+ default: string;
9
+ };
10
+ baseUrl: {
11
+ type: StringConstructor;
12
+ default: string;
13
+ };
14
+ title: {
15
+ type: StringConstructor;
16
+ default: string;
17
+ };
18
+ /**
19
+ * 大模型名称,对应 LLMTypes 中的 modelName
20
+ * 例如: 'szjy' | 'spark' | 'dify-openai' | 'siliconflow' 等
21
+ * 默认使用 'szjy'
22
+ */
23
+ llmName: {
24
+ type: StringConstructor;
25
+ default: string;
26
+ };
27
+ /**
28
+ * 消息后处理回调函数
29
+ * 在 AI 消息渲染完成后调用,可用于自定义处理渲染后的内容
30
+ * @param container - 消息容器的 DOM 元素
31
+ * @param textContent - 消息的文本内容
32
+ */
33
+ messagePostRender: {
34
+ type: FunctionConstructor;
35
+ default: null;
36
+ };
37
+ /**
38
+ * 额外配置参数,用于扩展供应商 API 调用
39
+ * 例如:{ projectId: 'xxx' } 供 Dify 等供应商使用
40
+ */
41
+ extraConfig: {
42
+ type: ObjectConstructor;
43
+ default: () => {};
44
+ };
45
+ /**
46
+ * 语音识别模型静态资源基础路径,默认为 '/sounds/'
47
+ */
48
+ soundBaseUrl: {
49
+ type: StringConstructor;
50
+ default: string;
51
+ };
52
+ /**
53
+ * Whisper Worker 脚本地址。不传则自动探测库产物与 /scripts/ai/whisper.worker.js
54
+ */
55
+ workerUrl: {
56
+ type: StringConstructor;
57
+ default: string;
58
+ };
59
+ /**
60
+ * 是否显示麦克风语音输入入口,默认关闭
61
+ */
62
+ enableVoiceInput: {
63
+ type: BooleanConstructor;
64
+ default: boolean;
65
+ };
66
+ /**
67
+ * 是否显示上传音频/视频识别入口,默认关闭
68
+ */
69
+ enableVoiceUpload: {
70
+ type: BooleanConstructor;
71
+ default: boolean;
72
+ };
73
+ }>, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
74
+ submit: (...args: any[]) => void;
75
+ }, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
76
+ token: {
77
+ type: StringConstructor;
78
+ default: string;
79
+ };
80
+ identifier: {
81
+ type: StringConstructor;
82
+ default: string;
83
+ };
84
+ baseUrl: {
85
+ type: StringConstructor;
86
+ default: string;
87
+ };
88
+ title: {
89
+ type: StringConstructor;
90
+ default: string;
91
+ };
92
+ /**
93
+ * 大模型名称,对应 LLMTypes 中的 modelName
94
+ * 例如: 'szjy' | 'spark' | 'dify-openai' | 'siliconflow' 等
95
+ * 默认使用 'szjy'
96
+ */
97
+ llmName: {
98
+ type: StringConstructor;
99
+ default: string;
100
+ };
101
+ /**
102
+ * 消息后处理回调函数
103
+ * 在 AI 消息渲染完成后调用,可用于自定义处理渲染后的内容
104
+ * @param container - 消息容器的 DOM 元素
105
+ * @param textContent - 消息的文本内容
106
+ */
107
+ messagePostRender: {
108
+ type: FunctionConstructor;
109
+ default: null;
110
+ };
111
+ /**
112
+ * 额外配置参数,用于扩展供应商 API 调用
113
+ * 例如:{ projectId: 'xxx' } 供 Dify 等供应商使用
114
+ */
115
+ extraConfig: {
116
+ type: ObjectConstructor;
117
+ default: () => {};
118
+ };
119
+ /**
120
+ * 语音识别模型静态资源基础路径,默认为 '/sounds/'
121
+ */
122
+ soundBaseUrl: {
123
+ type: StringConstructor;
124
+ default: string;
125
+ };
126
+ /**
127
+ * Whisper Worker 脚本地址。不传则自动探测库产物与 /scripts/ai/whisper.worker.js
128
+ */
129
+ workerUrl: {
130
+ type: StringConstructor;
131
+ default: string;
132
+ };
133
+ /**
134
+ * 是否显示麦克风语音输入入口,默认关闭
135
+ */
136
+ enableVoiceInput: {
137
+ type: BooleanConstructor;
138
+ default: boolean;
139
+ };
140
+ /**
141
+ * 是否显示上传音频/视频识别入口,默认关闭
142
+ */
143
+ enableVoiceUpload: {
144
+ type: BooleanConstructor;
145
+ default: boolean;
146
+ };
147
+ }>> & Readonly<{
148
+ onSubmit?: ((...args: any[]) => any) | undefined;
149
+ }>, {
150
+ baseUrl: string;
151
+ token: string;
152
+ identifier: string;
153
+ title: string;
154
+ llmName: string;
155
+ messagePostRender: Function;
156
+ extraConfig: Record<string, any>;
157
+ soundBaseUrl: string;
158
+ workerUrl: string;
159
+ enableVoiceInput: boolean;
160
+ enableVoiceUpload: boolean;
161
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
162
+ export default _default;
@@ -1 +1,2 @@
1
1
  export * from './AiChat/index';
2
+ export * from './AiChatPlain/index';
@@ -0,0 +1,21 @@
1
+ # 语音静态脚本
2
+
3
+ `pnpm build:voice` 会生成:
4
+
5
+ | 文件 | 大约体积 | 是否进 git | 何时更新 |
6
+ | :--- | ---: | :--- | :--- |
7
+ | `whisper.worker.js` | ~10 KB | 可以 | 改 `whisper.worker.ts` 协议时 |
8
+ | `transformers.runtime.js` | ~56 MB | 否 | 升级 `@huggingface/transformers` |
9
+ | `ort-wasm-simd-threaded.jsep.mjs` | ~45 KB | 否 | 随 onnxruntime-web 升级 |
10
+ | `ort-wasm-simd-threaded.jsep.wasm` | ~21 MB | 否 | 随 onnxruntime-web 升级 |
11
+
12
+ 日常改识别协议只提交 / 重拷 `whisper.worker.js`。大运行时与 ORT WASM 请加入宿主 `.gitignore`:
13
+
14
+ ```
15
+ public/scripts/ai/transformers.runtime.js
16
+ public/scripts/ai/ort-wasm*
17
+ public/scripts/ai/*.wasm
18
+ ```
19
+
20
+ `pnpm build:voice` / `pnpm dev` 会从 `node_modules/onnxruntime-web/dist` 自动同步 `ort-wasm*` 到本目录。缺这些文件时控制台会出现:
21
+ `no available backend found ... ort-wasm-simd-threaded.jsep.mjs`。
@@ -0,0 +1,388 @@
1
+ import { env, pipeline, WhisperTextStreamer } from "./transformers.runtime.js";
2
+ const DEFAULT_CACHE_DIR = "szjy-voice-models";
3
+ const FALLBACK_CACHE_NAME = "szjy-voice-cache-v1";
4
+ function isOPFSSupported() {
5
+ return typeof navigator !== "undefined" && !!navigator.storage && typeof navigator.storage.getDirectory === "function";
6
+ }
7
+ async function clearVoiceModelCache(dirName = DEFAULT_CACHE_DIR) {
8
+ let cleared = false;
9
+ if (isOPFSSupported()) {
10
+ try {
11
+ const root = await navigator.storage.getDirectory();
12
+ await root.removeEntry(dirName, { recursive: true });
13
+ cleared = true;
14
+ console.log(`[OPFS] 成功清空语音模型缓存目录: ${dirName}`);
15
+ } catch (err) {
16
+ if (err.name === "NotFoundError") {
17
+ cleared = true;
18
+ } else {
19
+ console.warn(`[OPFS] 清理目录 ${dirName} 异常:`, err);
20
+ }
21
+ }
22
+ }
23
+ if (typeof caches !== "undefined") {
24
+ try {
25
+ const cacheKeys = await caches.keys();
26
+ for (const key of cacheKeys) {
27
+ if (key.includes("transformers") || key.includes("szjy-voice") || key === FALLBACK_CACHE_NAME) {
28
+ await caches.delete(key);
29
+ cleared = true;
30
+ }
31
+ }
32
+ } catch (err) {
33
+ console.warn("[CacheStorage] 清除缓存异常:", err);
34
+ }
35
+ }
36
+ return cleared;
37
+ }
38
+ const PURE_HALLUCINATION = [
39
+ /^(谢谢(观看|收看)?|感謝觀看|感谢观看)[。.!!]*$/u,
40
+ /^(thank you( for watching)?|thanks for watching)[。.!!]*$/i,
41
+ /^(字幕(by|志愿者|組|组)?|subscribe|订阅|訂閱)[。.!!]*$/iu,
42
+ /^(请不吝点赞|請不吝點贊).*$/u,
43
+ /^(三十分|三十秒|三十分钟|三十分鐘)[。.!!]*$/u
44
+ ];
45
+ function collapseRepeatedPhrases(text) {
46
+ if (!text) return "";
47
+ let s = text.replace(/[ \t]+/g, " ").replace(/([一-龥])\s+(?=[一-龥])/g, "$1").trim();
48
+ if (!s) return "";
49
+ let prev = "";
50
+ let guard = 0;
51
+ while (s !== prev && guard++ < 12) {
52
+ prev = s;
53
+ for (let len = 12; len >= 2; len--) {
54
+ const re = new RegExp(`(.{${len}})\\1+`, "g");
55
+ s = s.replace(re, "$1");
56
+ }
57
+ s = s.replace(/(.)\1{3,}/g, "$1$1");
58
+ }
59
+ return s.trim();
60
+ }
61
+ function isPureHallucination(text) {
62
+ const t = text.trim();
63
+ if (!t) return true;
64
+ return PURE_HALLUCINATION.some((re) => re.test(t));
65
+ }
66
+ function clampTranscriptToAudio(text, durationSec) {
67
+ if (!text) return "";
68
+ const maxChars = Math.max(16, Math.round(Math.max(0, durationSec) * 8) + 12);
69
+ if (text.length <= maxChars) return text;
70
+ return text.slice(0, maxChars).trim();
71
+ }
72
+ function sanitizeTranscript(text, durationSec = 0) {
73
+ let cleaned = collapseRepeatedPhrases(text || "");
74
+ if (!cleaned) return "";
75
+ if (isPureHallucination(cleaned)) return "";
76
+ if (durationSec > 0) {
77
+ cleaned = clampTranscriptToAudio(cleaned, durationSec);
78
+ cleaned = collapseRepeatedPhrases(cleaned);
79
+ if (isPureHallucination(cleaned)) return "";
80
+ }
81
+ return cleaned;
82
+ }
83
+ if (env?.backends?.onnx?.wasm) {
84
+ env.backends.onnx.wasm.wasmPaths = new URL(
85
+ /* @vite-ignore */
86
+ "./",
87
+ import.meta.url
88
+ ).href;
89
+ }
90
+ let asrPipeline = null;
91
+ let isInitializing = false;
92
+ let transcribeQueue = [];
93
+ let transcribeRunning = false;
94
+ const cancelledTaskIds = /* @__PURE__ */ new Set();
95
+ const originalFetch = self.fetch.bind(self);
96
+ self.fetch = async (input, init) => {
97
+ const response = await originalFetch(input, init);
98
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input?.url || "";
99
+ const contentType = response.headers.get("content-type") || "";
100
+ if (response.ok && (contentType.includes("text/html") || contentType.includes("application/xhtml+xml")) && (url.includes("/sounds/") || url.endsWith(".json") || url.endsWith(".onnx") || url.endsWith(".txt") || url.endsWith(".wasm"))) {
101
+ return new Response("File not found", {
102
+ status: 404,
103
+ statusText: "Not Found",
104
+ headers: { "Content-Type": "text/plain" }
105
+ });
106
+ }
107
+ return response;
108
+ };
109
+ env.allowLocalModels = true;
110
+ env.useBrowserCache = typeof caches !== "undefined";
111
+ self.addEventListener("message", async (e) => {
112
+ const { type, data } = e.data;
113
+ switch (type) {
114
+ case "INIT":
115
+ await initPipeline(data);
116
+ break;
117
+ case "TRANSCRIBE":
118
+ await enqueueTranscribe(data);
119
+ break;
120
+ case "CANCEL":
121
+ if (typeof data?.taskId === "number") {
122
+ cancelledTaskIds.add(data.taskId);
123
+ if (cancelledTaskIds.size > 24) {
124
+ const first = cancelledTaskIds.values().next().value;
125
+ if (typeof first === "number") cancelledTaskIds.delete(first);
126
+ }
127
+ transcribeQueue = transcribeQueue.filter((item) => item?.taskId !== data.taskId);
128
+ }
129
+ break;
130
+ case "CLEAR_CACHE":
131
+ try {
132
+ await clearVoiceModelCache();
133
+ asrPipeline = null;
134
+ self.postMessage({ type: "CACHE_CLEARED" });
135
+ } catch (err) {
136
+ self.postMessage({ type: "ERROR", error: err.message || "清理缓存失败" });
137
+ }
138
+ break;
139
+ default:
140
+ console.warn("[Whisper Worker] 未知指令:", type);
141
+ }
142
+ });
143
+ async function sanitizeTransformersCache() {
144
+ if (typeof caches === "undefined") return;
145
+ try {
146
+ const cache = await caches.open("transformers-cache");
147
+ const requests = await cache.keys();
148
+ for (const req of requests) {
149
+ const resp = await cache.match(req);
150
+ if (resp) {
151
+ const ct = resp.headers.get("content-type") || "";
152
+ if (ct.includes("text/html") || ct.includes("application/xhtml+xml")) {
153
+ await cache.delete(req);
154
+ console.warn("[Whisper Worker] 清理了已污染的 HTML 缓存项:", req.url);
155
+ continue;
156
+ }
157
+ try {
158
+ const clone = resp.clone();
159
+ const text = await clone.text();
160
+ if (text.trim().startsWith("<")) {
161
+ await cache.delete(req);
162
+ console.warn("[Whisper Worker] 清理了已污染的 HTML 文本缓存项:", req.url);
163
+ }
164
+ } catch {
165
+ }
166
+ }
167
+ }
168
+ } catch (e) {
169
+ console.warn("[Whisper Worker] 检查缓存异常:", e);
170
+ }
171
+ }
172
+ async function checkLocalModelExists(baseUrl, modelId) {
173
+ try {
174
+ const normalized = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
175
+ const testUrl = `${normalized}${modelId}/config.json`;
176
+ const res = await fetch(testUrl, { method: "GET" });
177
+ const contentType = res.headers.get("content-type") || "";
178
+ if (res.ok && !contentType.includes("text/html") && !contentType.includes("application/xhtml+xml")) {
179
+ return true;
180
+ }
181
+ } catch {
182
+ }
183
+ return false;
184
+ }
185
+ async function initPipeline(config) {
186
+ if (asrPipeline) {
187
+ self.postMessage({ type: "READY" });
188
+ return;
189
+ }
190
+ if (isInitializing) return;
191
+ isInitializing = true;
192
+ env.useBrowserCache = typeof caches !== "undefined";
193
+ await sanitizeTransformersCache();
194
+ const modelId = config?.modelId || "onnx-community/whisper-base";
195
+ const soundBaseUrl = config?.soundBaseUrl || "/sounds/";
196
+ const remoteHost = config?.remoteHost || "https://hf-mirror.com";
197
+ const dtype = config?.dtype || "q8";
198
+ const hasLocal = await checkLocalModelExists(soundBaseUrl, modelId);
199
+ if (hasLocal) {
200
+ console.log(`[Whisper Worker] 检测到本地静态模型目录: ${soundBaseUrl}${modelId}`);
201
+ const normalizedUrl = soundBaseUrl.endsWith("/") ? soundBaseUrl : `${soundBaseUrl}/`;
202
+ env.allowLocalModels = true;
203
+ env.allowRemoteModels = false;
204
+ env.localModelPath = normalizedUrl;
205
+ } else {
206
+ console.log(`[Whisper Worker] 本地未检测到模型文件,将自镜像源 ${remoteHost} 加载/缓存模型...`);
207
+ env.allowLocalModels = false;
208
+ env.allowRemoteModels = true;
209
+ env.remoteHost = remoteHost;
210
+ env.remotePathTemplate = "{model}/resolve/{revision}/";
211
+ }
212
+ const progress_callback = (progressData) => {
213
+ self.postMessage({
214
+ type: "PROGRESS",
215
+ data: progressData
216
+ });
217
+ };
218
+ try {
219
+ const requestedDevice = config?.device || "wasm";
220
+ let device = "wasm";
221
+ if (requestedDevice === "webgpu" || requestedDevice === "auto") {
222
+ if (typeof navigator !== "undefined" && navigator.gpu) {
223
+ device = "webgpu";
224
+ }
225
+ }
226
+ try {
227
+ console.log(`[Whisper Worker] 正在使用 ${device} 加载模型: ${modelId}...`);
228
+ asrPipeline = await pipeline("automatic-speech-recognition", modelId, {
229
+ device,
230
+ dtype,
231
+ progress_callback
232
+ });
233
+ } catch (gpuError) {
234
+ console.warn("[Whisper Worker] 指定设备初始化失败,自动降级为 WASM CPU:", gpuError);
235
+ device = "wasm";
236
+ asrPipeline = await pipeline("automatic-speech-recognition", modelId, {
237
+ device: "wasm",
238
+ dtype,
239
+ progress_callback
240
+ });
241
+ }
242
+ isInitializing = false;
243
+ self.postMessage({ type: "READY", device });
244
+ } catch (error) {
245
+ isInitializing = false;
246
+ console.error("[Whisper Worker] 模型初始化失败:", error);
247
+ self.postMessage({
248
+ type: "ERROR",
249
+ error: error.message || "语音模型加载失败,请检查网络或静态资源配置。"
250
+ });
251
+ }
252
+ }
253
+ async function enqueueTranscribe(data) {
254
+ if (typeof data?.taskId === "number" && cancelledTaskIds.has(data.taskId)) {
255
+ return;
256
+ }
257
+ transcribeQueue.push(data);
258
+ if (transcribeRunning) return;
259
+ transcribeRunning = true;
260
+ while (transcribeQueue.length) {
261
+ const next = transcribeQueue.shift();
262
+ if (typeof next?.taskId === "number" && cancelledTaskIds.has(next.taskId)) {
263
+ continue;
264
+ }
265
+ await transcribeAudio(next);
266
+ }
267
+ transcribeRunning = false;
268
+ }
269
+ function getAudioStats(audio) {
270
+ let peak = 0;
271
+ let sumSq = 0;
272
+ for (let i = 0; i < audio.length; i++) {
273
+ const sample = audio[i];
274
+ const abs = Math.abs(sample);
275
+ if (abs > peak) peak = abs;
276
+ sumSq += sample * sample;
277
+ }
278
+ return { peak, rms: Math.sqrt(sumSq / Math.max(1, audio.length)) };
279
+ }
280
+ async function transcribeAudio(data) {
281
+ if (!asrPipeline) {
282
+ self.postMessage({
283
+ type: "ERROR",
284
+ taskId: data?.taskId,
285
+ error: "语音识别引擎尚未就绪,请先初始化。"
286
+ });
287
+ return;
288
+ }
289
+ const {
290
+ taskId,
291
+ audio,
292
+ language = "chinese",
293
+ task = "transcribe",
294
+ chunkIndex = 0,
295
+ isFinal = true,
296
+ mode = "append",
297
+ nativeChunking = false
298
+ } = data;
299
+ if (typeof taskId === "number" && cancelledTaskIds.has(taskId)) {
300
+ return;
301
+ }
302
+ try {
303
+ self.postMessage({ type: "TRANSCRIBING", taskId, chunkIndex });
304
+ const { peak, rms } = getAudioStats(audio);
305
+ if (peak < 0.018 || rms < 45e-4) {
306
+ self.postMessage({
307
+ type: "RESULT",
308
+ taskId,
309
+ chunkIndex,
310
+ isFinal,
311
+ silent: true,
312
+ mode,
313
+ text: ""
314
+ });
315
+ return;
316
+ }
317
+ if (peak > 0 && peak < 0.35) {
318
+ const gain = Math.min(1.8, 0.45 / peak);
319
+ for (let i = 0; i < audio.length; i++) {
320
+ audio[i] = Math.max(-1, Math.min(1, audio[i] * gain));
321
+ }
322
+ }
323
+ const durationSec = audio.length / 16e3;
324
+ const windowSec = Math.min(30, Math.max(0.5, durationSec));
325
+ const maxNewTokens = Math.max(24, Math.min(400, Math.round(windowSec * 12) + 16));
326
+ let streamedText = "";
327
+ const generateOptions = {
328
+ language: language || "chinese",
329
+ task: task || "transcribe",
330
+ return_timestamps: false,
331
+ do_sample: false,
332
+ temperature: 0,
333
+ repetition_penalty: 1.12,
334
+ no_repeat_ngram_size: 3,
335
+ max_new_tokens: maxNewTokens
336
+ };
337
+ if (nativeChunking || audio.length > 16e3 * 30) {
338
+ generateOptions.chunk_length_s = 30;
339
+ generateOptions.stride_length_s = 5;
340
+ }
341
+ try {
342
+ const tokenizer = asrPipeline.tokenizer;
343
+ if (tokenizer) {
344
+ generateOptions.streamer = new WhisperTextStreamer(tokenizer, {
345
+ skip_prompt: true,
346
+ callback_function: (piece) => {
347
+ if (!piece) return;
348
+ if (typeof taskId === "number" && cancelledTaskIds.has(taskId)) return;
349
+ streamedText += piece;
350
+ self.postMessage({
351
+ type: "PARTIAL",
352
+ taskId,
353
+ chunkIndex,
354
+ delta: piece,
355
+ text: sanitizeTranscript(streamedText, durationSec)
356
+ });
357
+ }
358
+ });
359
+ }
360
+ } catch (streamErr) {
361
+ console.warn("[Whisper Worker] 创建文本流失败,回退为整段输出:", streamErr);
362
+ }
363
+ const result = await asrPipeline(audio, generateOptions);
364
+ if (typeof taskId === "number" && cancelledTaskIds.has(taskId)) {
365
+ return;
366
+ }
367
+ const raw = (typeof result === "string" ? result : result?.text ?? streamedText ?? "").trim();
368
+ const text = sanitizeTranscript(raw, durationSec);
369
+ self.postMessage({
370
+ type: "RESULT",
371
+ taskId,
372
+ chunkIndex,
373
+ isFinal,
374
+ silent: !text,
375
+ mode,
376
+ text
377
+ });
378
+ } catch (err) {
379
+ console.error("[Whisper Worker] 语音识别推理出错:", err);
380
+ self.postMessage({
381
+ type: "ERROR",
382
+ taskId,
383
+ chunkIndex: data?.chunkIndex,
384
+ isFinal: data?.isFinal,
385
+ error: `识别失败: ${err.message || err}`
386
+ });
387
+ }
388
+ }
package/dist/style.css CHANGED
@@ -1,4 +1,4 @@
1
- .svg-icon-wrapper[data-v-490b11c1]{position:relative;overflow:hidden;vertical-align:middle}.svg-icon[data-v-490b11c1]{width:1em;height:1em;fill:currentColor;vertical-align:middle;display:inline-block}.ai-copy-btn-wrapper{cursor:pointer;width:74px;height:24px;border-radius:12px;box-sizing:border-box;padding:10px 14px;border:1px solid #D9D9D9;display:flex;align-items:center;justify-content:center;font-size:12px;color:#666;line-height:20px;text-align:justify;margin-top:6px}.ai-copy-btn-wrapper .copy-text{margin-left:4px}.markdown-wrapper{line-height:1.2}.markdown-wrapper h1{font-size:2em}.markdown-wrapper h2{font-size:1.5em}.markdown-wrapper h3{font-size:1.25em}.markdown-wrapper h4{font-size:1em}.markdown-wrapper h5{font-size:.875em}.markdown-wrapper h6{font-size:.85em}.markdown-wrapper h1,.markdown-wrapper h2,.markdown-wrapper h3,.markdown-wrapper h4,.markdown-wrapper h5,.markdown-wrapper h6{margin:0 auto;line-height:1.25}.markdown-wrapper ul,.markdown-wrapper ol{padding-left:1.5em;line-height:.8}.markdown-wrapper ul,.markdown-wrapper li,.markdown-wrapper ol{list-style-position:outside;white-space:normal}.markdown-wrapper ul{list-style-type:disc}.markdown-wrapper ol{list-style-type:decimal}.markdown-wrapper li{line-height:1.5}.markdown-wrapper ol ol,.markdown-wrapper ul ul{padding-left:20px}.markdown-wrapper hr{margin:16px 0}.markdown-wrapper a{color:#536fec;font-weight:bolder;text-decoration:underline;padding:0 3px}.markdown-wrapper p{line-height:1.4}.markdown-wrapper p>code{--at-apply: "bg-#e5e5e5";--at-apply: whitespace-pre mx-4px px-6px py-3px rounded-5px}.markdown-wrapper p img{display:inline-block}.markdown-wrapper li>p{line-height:2}.markdown-wrapper strong{font-weight:700}.markdown-wrapper blockquote{padding:10px;margin:20px 0;border-left:5px solid #ccc;background-color:#f9f9f9;color:#555}.markdown-wrapper blockquote>p{margin:0}.ai-panel{margin-right:20px;width:400px;height:804px;background:#fff;border-radius:8px;box-shadow:0 8px 24px #00000026;display:flex;flex-direction:column;overflow:hidden;background-image:url(/images/ai-panel-bg.png);background-size:100% auto;background-position:top;background-repeat:no-repeat;position:relative;transition:all .3s ease}.ai-panel :deep(.el-input-group__append){box-sizing:border-box}.ai-panel.drawer-mode{position:fixed;right:0;top:0;bottom:0;height:100vh;margin:0;border-radius:0;z-index:2000}.ai-panel .panel-header{padding:20px;background:transparent}.ai-panel .panel-header .header-actions{display:flex;justify-content:space-between;gap:12px;align-items:center}.ai-panel .panel-header .header-actions .header-title{font-family:PingFangSC,PingFang SC;font-weight:700;font-size:16px;color:#333;line-height:24px;text-align:left;font-style:normal}.ai-panel .panel-header .header-actions .action-icons{display:flex;gap:12px}.ai-panel .panel-header .header-actions .action-icon{font-size:20px;color:#666;cursor:pointer}.ai-panel .panel-header .header-actions .action-icon:hover{color:#333}.ai-panel .body-content{flex:1;overflow:hidden;padding:20px;display:flex;flex-direction:column}.ai-panel .body-content .welcome-content .title{display:flex;flex-direction:column;align-items:center;margin-bottom:8px}.ai-panel .body-content .welcome-content .title .panel-icon{width:64px;height:64px;margin-bottom:16px}.ai-panel .body-content .welcome-content .title .title-text{margin:0;font-size:24px;color:#333;font-weight:500}.ai-panel .body-content .welcome-content .subtitle{text-align:center;font-size:28px;color:#333;line-height:36px;letter-spacing:2px;margin-bottom:20px}.ai-panel .body-content .welcome-content .subtitle .highlight{margin-left:2px;background:linear-gradient(270deg,#e236ffcc,#0a84ff);-webkit-background-clip:text;color:transparent;font-weight:bolder}.ai-panel .body-content .quick-questions .question-item{padding:12px 16px;background:#f5f7fa;border-radius:8px;margin-bottom:12px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:all .3s}.ai-panel .body-content .quick-questions .question-item:hover{background:#e8f4ff;transform:translate(4px)}.ai-panel .body-content .quick-questions .question-item span{font-size:14px;color:#333}.ai-panel .body-content .quick-questions .question-item .el-icon{color:#666}.ai-panel .body-content .chat-messages{flex:1;overflow-y:auto;overflow-x:hidden;height:100%;display:flex;flex-direction:column}.ai-panel .body-content .chat-messages::-webkit-scrollbar{width:0;background:transparent}.ai-panel .body-content .chat-messages{scrollbar-width:none;-ms-overflow-style:none}.ai-panel .body-content .chat-messages .message-item{display:flex;align-items:flex-start;margin-bottom:20px;gap:12px}.ai-panel .body-content .chat-messages .message-item .message-avatar{width:40px;height:40px;border-radius:20px;flex-shrink:0}.ai-panel .body-content .chat-messages .message-item .user-avatar{background:#0a84ff;color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px}.ai-panel .body-content .chat-messages .message-item .message-content{padding:12px 16px;border-radius:2px;max-width:70%;word-break:break-word}.ai-panel .body-content .chat-messages .message-item.ai-message .message-content{background:#f5f7fa}.ai-panel .body-content .chat-messages .message-item.user-message{flex-direction:row-reverse}.ai-panel .body-content .chat-messages .message-item.user-message .message-content{background:#f1f2f3;border-radius:4px 4px 0;color:#333;font-size:14px}.ai-panel .panel-footer{padding:24px;background:#fff;position:relative}.ai-panel .panel-footer .loading-icon{position:absolute;top:-13px}.ai-panel .panel-footer .input-box .el-input-group{height:40px;border-radius:4px}.ai-panel .panel-footer .input-box :deep(.el-input__wrapper){box-shadow:none}.ai-panel .panel-footer .input-box :deep(.el-input-group__append){box-shadow:none;background:transparent;padding:8px 12px}.ai-panel .panel-footer .input-box .send-icon-box{display:flex;align-items:center;justify-content:center;cursor:pointer}.ai-panel .panel-footer .input-box .el-input-group__append{background-color:transparent}/*!
1
+ @charset "UTF-8";.svg-icon-wrapper[data-v-490b11c1]{position:relative;overflow:hidden;vertical-align:middle}.svg-icon[data-v-490b11c1]{width:1em;height:1em;fill:currentColor;vertical-align:middle;display:inline-block}.ai-copy-btn-wrapper{cursor:pointer;width:74px;height:24px;border-radius:12px;box-sizing:border-box;padding:10px 14px;border:1px solid #D9D9D9;display:flex;align-items:center;justify-content:center;font-size:12px;color:#666;line-height:20px;text-align:justify;margin-top:6px}.ai-copy-btn-wrapper .copy-text{margin-left:4px}.ai-thinking-block{margin-bottom:10px;border:1px solid #E8E8E8;border-radius:6px;background:#fafafa;overflow:hidden}.ai-thinking-block .thinking-header{display:flex;align-items:center;gap:6px;padding:7px 10px;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:12px;color:#666;transition:background-color .2s ease}.ai-thinking-block .thinking-header:hover{background-color:#f0f7ff;color:#1677ff}.ai-thinking-block .thinking-header:hover .thinking-toggle-hint{color:#1677ff}.ai-thinking-block .thinking-header .thinking-arrow{flex-shrink:0;transition:transform .2s ease}.ai-thinking-block .thinking-header .thinking-title{flex:1;text-align:left;font-weight:500;line-height:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ai-thinking-block .thinking-header .thinking-toggle-hint{flex-shrink:0;color:#999}.ai-thinking-block.is-expanded .thinking-arrow{transform:rotate(90deg)}.ai-thinking-block.is-streaming{border-color:#bae0ff;background:linear-gradient(135deg,#f0f7ff,#e6f4ff)}.ai-thinking-block.is-streaming .thinking-header .thinking-title{color:#1677ff;animation:thinking-breathe 1.2s ease-in-out infinite}.ai-thinking-block .thinking-body{max-height:280px;overflow-y:auto;padding:8px 12px 10px;border-top:1px dashed #E8E8E8;font-size:13px;color:#8c8c8c;line-height:1.6;word-break:break-word}.ai-thinking-block .thinking-body p{margin:0 0 4px}.ai-thinking-block .thinking-body p:last-child{margin-bottom:0}@keyframes thinking-breathe{0%,to{opacity:1}50%{opacity:.55}}.markdown-wrapper{font-size:14px;line-height:1.75;color:#333;word-break:break-word}.markdown-wrapper p{margin:0 0 10px}.markdown-wrapper p:last-child{margin-bottom:0}.markdown-wrapper h1,.markdown-wrapper h2,.markdown-wrapper h3,.markdown-wrapper h4,.markdown-wrapper h5,.markdown-wrapper h6{margin:14px 0 10px;font-weight:600;line-height:1.4}.markdown-wrapper h1:first-child,.markdown-wrapper h2:first-child,.markdown-wrapper h3:first-child,.markdown-wrapper h4:first-child,.markdown-wrapper h5:first-child,.markdown-wrapper h6:first-child{margin-top:0}.markdown-wrapper h1{font-size:18px}.markdown-wrapper h2{font-size:17px}.markdown-wrapper h3{font-size:16px}.markdown-wrapper h4,.markdown-wrapper h5,.markdown-wrapper h6{font-size:15px}.markdown-wrapper ul,.markdown-wrapper ol{margin:0 0 10px;padding-left:1.5em}.markdown-wrapper ul:last-child,.markdown-wrapper ol:last-child{margin-bottom:0}.markdown-wrapper li{margin:4px 0;line-height:1.7}.markdown-wrapper li>p{margin:0;line-height:1.7}.markdown-wrapper hr{margin:14px 0;border:none;border-top:1px solid #e8e8e8}.markdown-wrapper a{color:#536fec;font-weight:bolder;text-decoration:underline;padding:0 3px}.markdown-wrapper strong{font-weight:600}.markdown-wrapper code{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace}.markdown-wrapper p>code,.markdown-wrapper li>code,.markdown-wrapper td>code{background:#536fec14;color:#4761c6;padding:2px 6px;border-radius:4px;font-size:12.5px;white-space:pre-wrap}.markdown-wrapper pre.md-code-block{margin:0 0 10px;padding:12px 14px;background:#f6f8fa;border:1px solid #e8ecf2;border-radius:8px;overflow:auto;max-height:380px;font-size:12.5px;line-height:1.6}.markdown-wrapper pre.md-code-block:last-child{margin-bottom:0}.markdown-wrapper pre.md-code-block code{display:block;padding:0;background:transparent;color:#333;white-space:pre;font-size:inherit;line-height:inherit}.markdown-wrapper blockquote{padding:10px 14px;margin:10px 0;border-left:4px solid #c9d4f0;background-color:#f7f9fd;border-radius:0 6px 6px 0;color:#555}.markdown-wrapper blockquote>p{margin:0}.markdown-wrapper table{width:100%;margin:10px 0;border-collapse:collapse;font-size:13px}.markdown-wrapper table th,.markdown-wrapper table td{border:1px solid #e0e6ef;padding:6px 10px;text-align:left}.markdown-wrapper table th{background:#f2f5fa;font-weight:600}.markdown-wrapper table tr:nth-child(2n) td{background:#fafbfd}.markdown-wrapper img{display:inline-block;max-width:100%}/*!
2
2
  Theme: Default
3
3
  Description: Original highlight.js style
4
4
  Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
@@ -6,4 +6,4 @@
6
6
  Website: https://highlightjs.org/
7
7
  License: see project LICENSE
8
8
  Touched: 2021
9
- */pre code.hljs[data-v-33f89960]{display:block;overflow-x:auto;padding:1em}code.hljs[data-v-33f89960]{padding:3px 5px}.hljs[data-v-33f89960]{background:#f3f3f3;color:#444}.hljs-comment[data-v-33f89960]{color:#697070}.hljs-punctuation[data-v-33f89960],.hljs-tag[data-v-33f89960]{color:#444a}.hljs-tag .hljs-attr[data-v-33f89960],.hljs-tag .hljs-name[data-v-33f89960]{color:#444}.hljs-attribute[data-v-33f89960],.hljs-doctag[data-v-33f89960],.hljs-keyword[data-v-33f89960],.hljs-meta .hljs-keyword[data-v-33f89960],.hljs-name[data-v-33f89960],.hljs-selector-tag[data-v-33f89960]{font-weight:700}.hljs-deletion[data-v-33f89960],.hljs-number[data-v-33f89960],.hljs-quote[data-v-33f89960],.hljs-selector-class[data-v-33f89960],.hljs-selector-id[data-v-33f89960],.hljs-string[data-v-33f89960],.hljs-template-tag[data-v-33f89960],.hljs-type[data-v-33f89960]{color:#800}.hljs-section[data-v-33f89960],.hljs-title[data-v-33f89960]{color:#800;font-weight:700}.hljs-link[data-v-33f89960],.hljs-operator[data-v-33f89960],.hljs-regexp[data-v-33f89960],.hljs-selector-attr[data-v-33f89960],.hljs-selector-pseudo[data-v-33f89960],.hljs-symbol[data-v-33f89960],.hljs-template-variable[data-v-33f89960],.hljs-variable[data-v-33f89960]{color:#ab5656}.hljs-literal[data-v-33f89960]{color:#695}.hljs-addition[data-v-33f89960],.hljs-built_in[data-v-33f89960],.hljs-bullet[data-v-33f89960],.hljs-code[data-v-33f89960]{color:#397300}.hljs-meta[data-v-33f89960]{color:#1f7199}.hljs-meta .hljs-string[data-v-33f89960]{color:#38a}.hljs-emphasis[data-v-33f89960]{font-style:italic}.hljs-strong[data-v-33f89960]{font-weight:700}.ai-assistant{box-sizing:border-box;position:fixed;right:20px;bottom:40px;z-index:1000;display:flex;align-items:flex-end}.ai-assistant div{box-sizing:border-box}.ai-assistant .ai-icon{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;cursor:pointer;width:44px;padding:6px;transition:all .3s;height:114px;background-color:#fff;box-shadow:0 4px 16px 4px #00000008,0 2px 8px #0000000d,0 0 4px -2px #00000014;border-radius:22px;border:1px solid transparent;background-image:linear-gradient(#fff,#fff),linear-gradient(180deg,#60fffc,#0a84ff);background-origin:border-box;background-clip:padding-box,border-box}.ai-assistant .ai-icon:hover{transform:translateY(-2px);box-shadow:0 4px 16px #00000026}.ai-assistant .ai-icon img{width:32px;height:32px;margin-bottom:6px}.ai-assistant .ai-icon .ai-icon-text{display:inline-block;width:20px;font-size:16px;line-height:18px;font-weight:700;color:#333}.ai-assistant .ai-icon:hover .close-icon{display:flex}.ai-assistant .ai-icon .close-icon{display:none;position:absolute;top:-6px;right:-6px;width:16px;height:16px;background:#fff;border-radius:50%;align-items:center;justify-content:center;box-shadow:0 2px 4px #0000001a;color:#666;cursor:pointer;z-index:1}.ai-assistant .ai-icon .close-icon:hover{color:#333;background:#f5f5f5}
9
+ */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.slide-enter-active,.slide-leave-active{transition:transform .3s ease}.slide-enter-from,.slide-leave-to{transform:translate(30px)}.ai-panel{margin-right:20px;width:400px;height:804px;background:#fff;border-radius:8px;box-shadow:0 8px 24px #00000026;display:flex;flex-direction:column;overflow:hidden;background-image:url(/images/ai-panel-bg.png);background-size:100% auto;background-position:top;background-repeat:no-repeat;position:relative;transition:all .3s ease}.ai-panel :deep(.el-input-group__append){box-sizing:border-box}.ai-panel.drawer-mode{position:fixed;right:0;top:0;bottom:0;height:100vh;margin:0;border-radius:0;z-index:2000}.ai-panel.embedded-mode{width:100%;height:100%;margin:0;box-shadow:none;border-radius:0}.ai-panel .panel-header{padding:20px;background:transparent}.ai-panel .panel-header .header-actions{display:flex;justify-content:space-between;gap:12px;align-items:center}.ai-panel .panel-header .header-actions .header-title{font-family:PingFangSC,PingFang SC;font-weight:700;font-size:16px;color:#333;line-height:24px;text-align:left;font-style:normal}.ai-panel .panel-header .header-actions .action-icons{display:flex;gap:12px}.ai-panel .panel-header .header-actions .action-icon{font-size:20px;color:#666;cursor:pointer}.ai-panel .panel-header .header-actions .action-icon:hover{color:#333}.ai-panel .body-content{flex:1;overflow:hidden;padding:20px;display:flex;flex-direction:column}.ai-panel .body-content .welcome-content .title{display:flex;flex-direction:column;align-items:center;margin-bottom:8px}.ai-panel .body-content .welcome-content .title .panel-icon{width:64px;height:64px;margin-bottom:16px}.ai-panel .body-content .welcome-content .title .title-text{margin:0;font-size:24px;color:#333;font-weight:500}.ai-panel .body-content .welcome-content .subtitle{text-align:center;font-size:28px;color:#333;line-height:36px;letter-spacing:2px;margin-bottom:20px}.ai-panel .body-content .welcome-content .subtitle .highlight{margin-left:2px;background:linear-gradient(270deg,#e236ffcc,#0a84ff);-webkit-background-clip:text;color:transparent;font-weight:bolder}.ai-panel .body-content .quick-questions .question-item{padding:12px 16px;background:#f5f7fa;border-radius:8px;margin-bottom:12px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:all .3s}.ai-panel .body-content .quick-questions .question-item:hover{background:#e8f4ff;transform:translate(4px)}.ai-panel .body-content .quick-questions .question-item span{font-size:14px;color:#333}.ai-panel .body-content .quick-questions .question-item .el-icon{color:#666}.ai-panel .body-content .chat-messages{flex:1;overflow-y:auto;overflow-x:hidden;height:100%;display:flex;flex-direction:column}.ai-panel .body-content .chat-messages::-webkit-scrollbar{width:0;background:transparent}.ai-panel .body-content .chat-messages{scrollbar-width:none;-ms-overflow-style:none}.ai-panel .body-content .chat-messages .message-item{display:flex;align-items:flex-start;margin-bottom:20px;gap:12px}.ai-panel .body-content .chat-messages .message-item .message-avatar{width:40px;height:40px;border-radius:20px;flex-shrink:0}.ai-panel .body-content .chat-messages .message-item .user-avatar{background:#0a84ff;color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px}.ai-panel .body-content .chat-messages .message-item .message-content{padding:12px 16px;border-radius:2px;max-width:70%;word-break:break-word}.ai-panel .body-content .chat-messages .message-item.ai-message .message-content{background:#f5f7fa;font-size:14px}.ai-panel .body-content .chat-messages .message-item.user-message{flex-direction:row-reverse}.ai-panel .body-content .chat-messages .message-item.user-message .message-content{background:#f1f2f3;border-radius:4px 4px 0;color:#333;font-size:14px}.ai-panel .panel-footer{padding:24px;background:#fff;position:relative}.ai-panel .panel-footer .loading-icon{position:absolute;top:-13px}.ai-panel .panel-footer .voice-progress-banner{margin-bottom:12px;padding:10px 14px;background:linear-gradient(135deg,#f0f7ff,#e6f4ff);border:1px solid #bae0ff;border-radius:8px;display:flex;flex-direction:column;gap:6px}.ai-panel .panel-footer .voice-progress-banner .progress-info{display:flex;align-items:center;gap:8px;font-size:13px;color:#0958d9}.ai-panel .panel-footer .voice-progress-banner .progress-info .progress-icon{font-size:14px}.ai-panel .panel-footer .voice-progress-banner .progress-info .progress-text{flex:1;font-weight:500}.ai-panel .panel-footer .voice-progress-banner .progress-info .progress-percent{font-weight:700;font-family:monospace}.ai-panel .panel-footer .voice-progress-banner .progress-track{height:6px;background:#d9d9d9;border-radius:3px;overflow:hidden}.ai-panel .panel-footer .voice-progress-banner .progress-track .progress-bar{height:100%;background:linear-gradient(90deg,#1677ff,#4096ff);border-radius:3px;transition:width .2s ease}.ai-panel .panel-footer .voice-recording-panel{min-height:44px;padding:8px 14px;background:linear-gradient(135deg,#f6ffed,#e6f7ff);border:1px solid #91caff;border-radius:6px;display:flex;flex-direction:column;gap:8px;box-shadow:0 2px 8px #1677ff14}.ai-panel .panel-footer .voice-recording-panel .recording-main{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:28px}.ai-panel .panel-footer .voice-recording-panel .live-transcript{width:100%;max-height:72px;overflow-y:auto;padding:6px 8px;background:#ffffffc7;border-radius:4px;font-size:13px;line-height:1.55;color:#1677ff;word-break:break-all}.ai-panel .panel-footer .voice-recording-panel .live-transcript.empty{color:#8c8c8c}.ai-panel .panel-footer .voice-recording-panel .live-transcript .caret{display:inline-block;margin-left:1px;font-weight:400;color:#1677ff;animation:caret-blink 1s step-end infinite}.ai-panel .panel-footer .voice-recording-panel .recording-status{display:flex;align-items:center;gap:8px}.ai-panel .panel-footer .voice-recording-panel .recording-status .recording-pulse{width:10px;height:10px;border-radius:50%;background:#ff4d4f;box-shadow:0 0 #ff4d4f66;animation:pulse 1.5s infinite}.ai-panel .panel-footer .voice-recording-panel .recording-status .recording-pulse.transcribing{background:#1890ff;box-shadow:0 0 #1890ff66}.ai-panel .panel-footer .voice-recording-panel .recording-status .recording-time{font-family:monospace;font-weight:700;font-size:13px;color:#ff4d4f}.ai-panel .panel-footer .voice-recording-panel .recording-status .recording-hint{font-size:13px;color:#333}.ai-panel .panel-footer .voice-recording-panel .recording-status .volume-waves{display:flex;align-items:center;gap:3px;height:30px;margin-left:4px}.ai-panel .panel-footer .voice-recording-panel .recording-status .volume-waves .wave{width:3px;background:#1890ff;border-radius:2px;transition:height .08s ease}.ai-panel .panel-footer .voice-recording-panel .recording-actions{display:flex;align-items:center;gap:8px}.ai-panel .panel-footer .voice-recording-panel .recording-actions .voice-cancel-btn{color:#8c8c8c;border-color:#d9d9d9}.ai-panel .panel-footer .voice-recording-panel .recording-actions .voice-cancel-btn:hover{color:#ff4d4f;border-color:#ff4d4f;background:#fff1f0}.ai-panel .panel-footer .voice-recording-panel .recording-actions .voice-finish-btn{background:#1677ff;border-color:#1677ff;padding:6px 14px;font-weight:500}.ai-panel .panel-footer .voice-recording-panel .recording-actions .voice-cancel-transcribe-btn{color:#666;border-color:#d9d9d9;background:#fff;font-size:12px;padding:4px 10px;height:28px;transition:all .2s}.ai-panel .panel-footer .voice-recording-panel .recording-actions .voice-cancel-transcribe-btn:hover{color:#ff4d4f;border-color:#ff4d4f;background:#fff1f0}.ai-panel .panel-footer .voice-playback-bar{margin-bottom:8px;height:36px;padding:0 12px;background:#f0f7ff;border:1px solid #bae0ff;border-radius:6px;display:flex;align-items:center;justify-content:space-between;transition:all .2s ease}.ai-panel .panel-footer .voice-playback-bar .playback-main{display:flex;align-items:center;gap:8px;cursor:pointer;-webkit-user-select:none;user-select:none}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-play-icon{width:22px;height:22px;border-radius:50%;background:#1677ff;color:#fff;display:flex;align-items:center;justify-content:center;transition:transform .15s}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-play-icon:hover{transform:scale(1.1)}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-title{font-size:13px;color:#1677ff;font-weight:500}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-time{font-size:12px;color:#8c8c8c;font-family:monospace}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-waves{display:flex;align-items:flex-end;gap:2px;height:14px;margin-left:4px}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-waves .wave-bar{width:2px;background:#1677ff;border-radius:1px;animation:soundWave .8s ease-in-out infinite alternate}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-waves .wave-bar:nth-child(1){height:6px;animation-delay:0s}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-waves .wave-bar:nth-child(2){height:12px;animation-delay:.2s}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-waves .wave-bar:nth-child(3){height:8px;animation-delay:.4s}.ai-panel .panel-footer .voice-playback-bar .playback-main .playback-waves .wave-bar:nth-child(4){height:14px;animation-delay:.1s}.ai-panel .panel-footer .voice-playback-bar .playback-close{cursor:pointer;color:#8c8c8c;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;transition:all .2s}.ai-panel .panel-footer .voice-playback-bar .playback-close:hover{color:#ff4d4f;background:#fff1f0}.ai-panel .panel-footer .input-box .el-input-group{height:40px;border-radius:4px}.ai-panel .panel-footer .input-box :deep(.el-input__wrapper){box-shadow:none}.ai-panel .panel-footer .input-box :deep(.el-input-group__append){box-shadow:none;background:transparent;padding:8px 12px}.ai-panel .panel-footer .input-box .input-actions-box{display:flex;align-items:center;gap:12px}.ai-panel .panel-footer .input-box .input-actions-box .voice-upload-btn{display:flex;align-items:center;justify-content:center;cursor:pointer;color:#666;transition:all .2s;padding:4px;border-radius:4px}.ai-panel .panel-footer .input-box .input-actions-box .voice-upload-btn:hover{color:#1677ff;background:#f0f7ff;transform:scale(1.08)}.ai-panel .panel-footer .input-box .input-actions-box .voice-mic-btn{display:flex;align-items:center;justify-content:center;cursor:pointer;color:#666;transition:all .2s;padding:4px;border-radius:4px}.ai-panel .panel-footer .input-box .input-actions-box .voice-mic-btn:hover{color:#1677ff;background:#f0f7ff;transform:scale(1.08)}.ai-panel .panel-footer .input-box .input-actions-box .voice-mic-btn.loading-anim{color:#1677ff}.ai-panel .panel-footer .input-box .input-actions-box .voice-mic-btn.loading-anim svg{animation:mic-breathe 1.2s ease-in-out infinite;transform-origin:center}.ai-panel .panel-footer .input-box .input-actions-box .send-icon-box{display:flex;align-items:center;justify-content:center;cursor:pointer}.ai-panel .panel-footer .input-box .el-input-group__append{background-color:transparent}@keyframes soundWave{0%{height:3px}to{height:14px}}@keyframes pulse{0%{box-shadow:0 0 #ff4d4fb3}70%{box-shadow:0 0 0 8px #ff4d4f00}to{box-shadow:0 0 #ff4d4f00}}@keyframes caret-blink{0%,to{opacity:1}50%{opacity:0}}@keyframes mic-breathe{0%,to{transform:scale(1);opacity:1}50%{transform:scale(1.22);opacity:.72}}.ai-assistant{box-sizing:border-box;position:fixed;right:20px;bottom:40px;z-index:1000;display:flex;align-items:flex-end}.ai-assistant div{box-sizing:border-box}.ai-assistant .ai-icon{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;cursor:pointer;width:44px;padding:6px;transition:all .3s;height:114px;background-color:#fff;box-shadow:0 4px 16px 4px #00000008,0 2px 8px #0000000d,0 0 4px -2px #00000014;border-radius:22px;border:1px solid transparent;background-image:linear-gradient(#fff,#fff),linear-gradient(180deg,#60fffc,#0a84ff);background-origin:border-box;background-clip:padding-box,border-box}.ai-assistant .ai-icon:hover{transform:translateY(-2px);box-shadow:0 4px 16px #00000026}.ai-assistant .ai-icon img{width:32px;height:32px;margin-bottom:6px}.ai-assistant .ai-icon .ai-icon-text{display:inline-block;width:20px;font-size:16px;line-height:18px;font-weight:700;color:#333}.ai-assistant .ai-icon:hover .close-icon{display:flex}.ai-assistant .ai-icon .close-icon{display:none;position:absolute;top:-6px;right:-6px;width:16px;height:16px;background:#fff;border-radius:50%;align-items:center;justify-content:center;box-shadow:0 2px 4px #0000001a;color:#666;cursor:pointer;z-index:1}.ai-assistant .ai-icon .close-icon:hover{color:#333;background:#f5f5f5}.ai-chat-plain{box-sizing:border-box;position:relative;width:100%;height:100%}.ai-chat-plain div{box-sizing:border-box}