@siact/sime-x-vue 0.0.4 → 0.0.5

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.
@@ -337,6 +337,7 @@ const _hoisted_13 = {
337
337
  };
338
338
  const _hoisted_14 = ["d"];
339
339
  const _hoisted_15 = ["src"];
340
+ const FAB_SAFE_GAP = 24;
340
341
  const _sfc_main = /* @__PURE__ */ defineComponent({
341
342
  __name: "sime-x",
342
343
  props: {
@@ -538,6 +539,46 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
538
539
  y: Math.max(margin, Math.min(y, maxY))
539
540
  };
540
541
  };
542
+ const getOverlapArea = (rectA, rectB) => {
543
+ const overlapX = Math.max(0, Math.min(rectA.right, rectB.right) - Math.max(rectA.left, rectB.left));
544
+ const overlapY = Math.max(0, Math.min(rectA.bottom, rectB.bottom) - Math.max(rectA.top, rectB.top));
545
+ return overlapX * overlapY;
546
+ };
547
+ const keepDistanceFromFab = (x, y, width, height) => {
548
+ if (!fabRef.value) return { x, y };
549
+ const fabRect = fabRef.value.getBoundingClientRect();
550
+ const safeFabRect = {
551
+ left: fabRect.left - FAB_SAFE_GAP,
552
+ right: fabRect.right + FAB_SAFE_GAP,
553
+ top: fabRect.top - FAB_SAFE_GAP,
554
+ bottom: fabRect.bottom + FAB_SAFE_GAP
555
+ };
556
+ const candidates = [
557
+ { x, y },
558
+ { x: safeFabRect.left - width - FAB_SAFE_GAP, y },
559
+ { x: safeFabRect.right + FAB_SAFE_GAP, y },
560
+ { x, y: safeFabRect.top - height - FAB_SAFE_GAP },
561
+ { x, y: safeFabRect.bottom + FAB_SAFE_GAP }
562
+ ];
563
+ let best = validatePosition(candidates[0].x, candidates[0].y, width, height);
564
+ let bestOverlap = getOverlapArea(
565
+ { left: best.x, right: best.x + width, top: best.y, bottom: best.y + height },
566
+ safeFabRect
567
+ );
568
+ for (let i = 1; i < candidates.length; i++) {
569
+ const validated = validatePosition(candidates[i].x, candidates[i].y, width, height);
570
+ const overlap = getOverlapArea(
571
+ { left: validated.x, right: validated.x + width, top: validated.y, bottom: validated.y + height },
572
+ safeFabRect
573
+ );
574
+ if (overlap < bestOverlap) {
575
+ best = validated;
576
+ bestOverlap = overlap;
577
+ if (overlap === 0) break;
578
+ }
579
+ }
580
+ return best;
581
+ };
541
582
  const calculateInitialPosition = () => {
542
583
  if (!fabRef.value) return;
543
584
  const fabRect = fabRef.value.getBoundingClientRect();
@@ -586,16 +627,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
586
627
  }
587
628
  }
588
629
  const validated = validatePosition(x, y, dialogWidth, dialogHeight);
589
- position.x = validated.x;
590
- position.y = validated.y;
630
+ const separated = keepDistanceFromFab(validated.x, validated.y, dialogWidth, dialogHeight);
631
+ position.x = separated.x;
632
+ position.y = separated.y;
591
633
  };
592
634
  const toggleCollapse = async () => {
593
635
  isCollapsed.value = !isCollapsed.value;
594
636
  nextTick(() => {
595
637
  const currentHeight = isCollapsed.value ? 60 : containerHeight.value;
596
638
  const validated = validatePosition(position.x, position.y, containerWidth.value, currentHeight);
597
- position.x = validated.x;
598
- position.y = validated.y;
639
+ const separated = keepDistanceFromFab(validated.x, validated.y, containerWidth.value, currentHeight);
640
+ position.x = separated.x;
641
+ position.y = separated.y;
599
642
  });
600
643
  };
601
644
  const drag = reactive({
@@ -631,6 +674,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
631
674
  if (state) {
632
675
  visible.value = true;
633
676
  positionReady.value = false;
677
+ emit("wakeUp", true);
634
678
  await nextTick();
635
679
  if (isFirstOpen.value) {
636
680
  calculateInitialPosition();
@@ -642,8 +686,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
642
686
  containerWidth.value,
643
687
  isCollapsed.value ? 60 : containerHeight.value
644
688
  );
645
- position.x = validated.x;
646
- position.y = validated.y;
689
+ const separated = keepDistanceFromFab(
690
+ validated.x,
691
+ validated.y,
692
+ containerWidth.value,
693
+ isCollapsed.value ? 60 : containerHeight.value
694
+ );
695
+ position.x = separated.x;
696
+ position.y = separated.y;
647
697
  }
648
698
  await nextTick();
649
699
  positionReady.value = true;
@@ -698,7 +748,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
698
748
  ref_key: "fabRef",
699
749
  ref: fabRef,
700
750
  class: "assistant-fab",
701
- onClick: _cache[0] || (_cache[0] = ($event) => toggleDialog(true))
751
+ onClick: _cache[0] || (_cache[0] = ($event) => toggleDialog(!visible.value))
702
752
  }, [
703
753
  !isProcessing.value ? (openBlock(), createBlock(VoiceStatus, {
704
754
  key: 0,
@@ -943,7 +993,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
943
993
  }
944
994
  });
945
995
 
946
- const simeX = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-73306d13"]]);
996
+ const simeX = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-246dc696"]]);
947
997
 
948
998
  export { _sfc_main$3 as AiChatbotProvider, simeX as AiChatbotX, AiChatbotXKey, clientCommandKey, injectStrict };
949
999
  //# sourceMappingURL=sime-x-vue.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sime-x-vue.mjs","sources":["../src/injection-key.ts","../src/types.ts","../src/components/sime-provider.vue","../src/components/voice-status.vue","../src/components/execution-status.vue","../src/lib/utils.ts","../src/components/sime-x.vue"],"sourcesContent":["import type { InjectionKey } from 'vue'\r\nimport type { AiChatbotXContext } from './types'\r\nimport { inject } from 'vue'\r\n\r\nexport const AiChatbotXKey: InjectionKey<AiChatbotXContext> = Symbol('sime-x')\r\n\r\nexport function injectStrict<T>(key: InjectionKey<T>): T\r\nexport function injectStrict<T>(key: InjectionKey<T>, defaultValue: T, treatDefaultAsFactory?: false): T\r\nexport function injectStrict<T>(key: InjectionKey<T>, defaultValue: T | (() => T), treatDefaultAsFactory: true): T\r\nexport function injectStrict<T>(key: InjectionKey<T>, defaultValue?: T | (() => T), treatDefaultAsFactory?: boolean): T {\r\n let result: T | undefined\r\n\r\n if (defaultValue === undefined) {\r\n result = inject(key) as T | undefined\r\n }\r\n else if (treatDefaultAsFactory === true) {\r\n result = inject(key, defaultValue as T | (() => T), true)\r\n }\r\n else {\r\n result = inject(key, defaultValue as T, false)\r\n }\r\n\r\n if (!result) {\r\n throw new Error(`Could not resolve ${key.description}`)\r\n }\r\n return result\r\n}\r\n","import type { CommandDefinition, DiscoveredCommand } from \"@siact/sime-bridge\";\r\n\r\nexport enum clientCommandKey {\r\n SET_THEME = \"SiMeAgent_setTheme\",\r\n APPEND_MESSAGE = \"SiMeAgent_appendMessage\",\r\n WAKE = \"SiMeAgent_wake\",\r\n TRANSITION = \"SiMeAgent_transition\",\r\n TRANSITION_END = \"SiMeAgent_transition_end\",\r\n START_NEW_CONVERSATION = \"SiMeAgent_startNewConversation\",\r\n RECOGNITION = 'SiMeAgent_recognition'\r\n}\r\n\r\nexport interface VoiceConfig {\r\n appId: string;\r\n apiKey: string;\r\n websocketUrl: string;\r\n}\r\n\r\nexport interface AiChatbotXContext {\r\n chatbotUrl: () => string;\r\n appId: () => string;\r\n appToken: () => string;\r\n voiceConfig: () => VoiceConfig;\r\n registerCommand: (cmd: CommandDefinition) => void;\r\n unregisterCommand: (name: string) => void;\r\n clientCommand: () => Promise<DiscoveredCommand[]>;\r\n hostCommads: () => Promise<DiscoveredCommand[]>;\r\n appendMessage: (message: string) => Promise<void>;\r\n setTheme: (theme: string) => Promise<void>;\r\n weak: () => Promise<void>;\r\n startListening: () => Promise<void>;\r\n stopListening: () => Promise<void>;\r\n toggleCollapse: () => Promise<void>;\r\n openDialog: () => Promise<void>;\r\n closeDialog: () => Promise<void>;\r\n registerVoiceMethods: (methods: {\r\n start: () => Promise<void>;\r\n stop: () => Promise<void>;\r\n toggleCollapse: () => Promise<void>;\r\n openDialog: () => Promise<void>;\r\n closeDialog: () => Promise<void>;\r\n }) => void;\r\n startNewConversation: () => Promise<void>;\r\n setIframeElement: (iframe: HTMLIFrameElement) => void;\r\n recognition: (message: string, commands: DiscoveredCommand[]) => Promise<any>;\r\n executeCommand: (commandName: string, args?: any[]) => Promise<any>;\r\n}\r\n","<template>\r\n <slot></slot>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { provide, shallowRef } from 'vue';\r\nimport { HostBridge, CommandDefinition } from '@siact/sime-bridge';\r\nimport { AiChatbotXKey } from '../injection-key';\r\nimport { clientCommandKey, VoiceConfig } from '../types';\r\n\r\nconst props = defineProps<{\r\n /** 项目名称 */\r\n project: string;\r\n /** 项目描述 */\r\n description?: string;\r\n /** 是否开启调试模式 */\r\n debug?: boolean;\r\n /** iframeUrl */\r\n chatbotUrl: string;\r\n /** appId */\r\n appId: string;\r\n /** appToken */\r\n appToken: string;\r\n /** 语音配置 */\r\n voiceConfig: VoiceConfig;\r\n}>();\r\n\r\nconst hostBridge = shallowRef<HostBridge>(new HostBridge());\r\n\r\nconst startListeningRef = shallowRef<() => Promise<void>>(async () => {});\r\nconst stopListeningRef = shallowRef<() => Promise<void>>(async () => {});\r\nconst toggleCollapseRef = shallowRef<() => Promise<void>>(async () => {});\r\nconst openDialogRef = shallowRef<() => Promise<void>>(async () => {});\r\nconst closeDialogRef = shallowRef<() => Promise<void>>(async () => {});\r\n\r\nprovide(AiChatbotXKey, {\r\n chatbotUrl: () => props.chatbotUrl,\r\n appId: () => props.appId,\r\n appToken: () => props.appToken,\r\n voiceConfig: () => props.voiceConfig,\r\n startListening: () => startListeningRef.value(),\r\n stopListening: () => stopListeningRef.value(),\r\n toggleCollapse: () => toggleCollapseRef.value(),\r\n openDialog: () => openDialogRef.value(),\r\n closeDialog: () => closeDialogRef.value(),\r\n registerVoiceMethods: (methods: {\r\n start: () => Promise<void>;\r\n stop: () => Promise<void>;\r\n toggleCollapse: () => Promise<void>;\r\n openDialog: () => Promise<void>;\r\n closeDialog: () => Promise<void>;\r\n }) => {\r\n startListeningRef.value = methods.start;\r\n stopListeningRef.value = methods.stop;\r\n toggleCollapseRef.value = methods.toggleCollapse;\r\n openDialogRef.value = methods.openDialog;\r\n closeDialogRef.value = methods.closeDialog;\r\n },\r\n clientCommand: () => hostBridge.value.clientCommands(),\r\n hostCommads: () => hostBridge.value.hostCommands(),\r\n registerCommand: (cmd: CommandDefinition) => {\r\n hostBridge.value.registerCommand(cmd);\r\n },\r\n unregisterCommand: (name) => {\r\n hostBridge.value.unregisterCommand(name);\r\n },\r\n async appendMessage(message) {\r\n await hostBridge.value.executeClientCommand(clientCommandKey.APPEND_MESSAGE, [message]);\r\n },\r\n async setTheme(theme) {\r\n await hostBridge.value.executeClientCommand(clientCommandKey.SET_THEME, [theme]);\r\n },\r\n async weak() {\r\n await hostBridge.value.executeClientCommand(clientCommandKey.WAKE);\r\n },\r\n async startNewConversation() {\r\n await hostBridge.value.executeClientCommand(clientCommandKey.START_NEW_CONVERSATION);\r\n },\r\n setIframeElement(iframe) {\r\n hostBridge.value.setIframe(iframe);\r\n },\r\n async recognition(message, commands) {\r\n return await hostBridge.value.executeClientCommand(clientCommandKey.RECOGNITION, [message, commands]);\r\n },\r\n async executeCommand(commandName, args = []) {\r\n return await hostBridge.value.executeCommand(commandName, args);\r\n },\r\n});\r\n</script>\r\n","<template>\r\n <transition name=\"voice-panel\">\r\n <!-- Main Container -->\r\n <div v-if=\"status === 'wake' || isTranscribing\" class=\"voice-status-wrapper\" :class=\"currentMode\">\r\n <!-- Visual Indicator Section -->\r\n <div class=\"indicator-container\">\r\n <!-- Ambient Glow (Background) -->\r\n <div class=\"ambient-glow\"></div>\r\n\r\n <!-- Animated Ripples -->\r\n <div class=\"ripple-layer\">\r\n <div class=\"ripple delay-1\"></div>\r\n <div class=\"ripple delay-2\"></div>\r\n <div class=\"ripple delay-3\"></div>\r\n </div>\r\n\r\n <!-- Core Icon Circle -->\r\n <div class=\"icon-core\">\r\n <!-- Unified Microphone Icon -->\r\n <svg\r\n class=\"mic-icon\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <!-- Microphone Capsule (Animates on voice) -->\r\n <rect x=\"9\" y=\"2\" width=\"6\" height=\"12\" rx=\"3\" class=\"mic-capsule\" />\r\n <!-- Stand/Base -->\r\n <path d=\"M5 10C5 13.866 8.13401 17 12 17C15.866 17 19 13.866 19 10\" class=\"mic-stand\" />\r\n <path d=\"M12 17V21M8 21H16\" class=\"mic-base\" />\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n <!-- Content Section -->\r\n <div class=\"content-container\">\r\n <!-- Status Header -->\r\n <div class=\"status-header\">\r\n <span class=\"status-text\">{{ statusLabel }}</span>\r\n </div>\r\n\r\n <!-- Transcription Text (Animated Entry) -->\r\n <div class=\"text-window\" :class=\"{ 'has-text': !!transcriptionText }\">\r\n <transition name=\"fade-slide\" mode=\"out-in\">\r\n <p v-if=\"transcriptionText\" class=\"transcription-content\">\r\n {{ transcriptionText }}\r\n </p>\r\n <p v-else-if=\"status === 'wake'\" class=\"placeholder-text\">Listening...</p>\r\n </transition>\r\n </div>\r\n </div>\r\n </div>\r\n </transition>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from 'vue';\r\ntype VoiceStatus = 'wake' | 'listening' | 'standby';\r\n\r\nconst props = defineProps<{\r\n status: VoiceStatus;\r\n transcriptionText?: string;\r\n isTranscribing?: boolean;\r\n}>();\r\n\r\n// Determine the current visual mode\r\nconst currentMode = computed(() => {\r\n if (props.isTranscribing) return 'mode-transcribing';\r\n if (props.status === 'wake') return 'mode-wake';\r\n return 'mode-standby';\r\n});\r\n\r\n// Dynamic label text\r\nconst statusLabel = computed(() => {\r\n if (props.isTranscribing) return '正在聆听您的问题...';\r\n if (props.status === 'wake') return '您好,有什么可以帮助您的吗?';\r\n return 'Standby';\r\n});\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n@use 'sass:color';\r\n\r\n/* --- Design Tokens --- */\r\n$c-wake: #10b981; // Emerald for Wake\r\n$c-transcribe: #3b82f6; // Blue for Transcribing\r\n$bg-glass: rgba(15, 23, 42, 0.85);\r\n$border-glass: rgba(255, 255, 255, 0.1);\r\n$ease-smooth: cubic-bezier(0.25, 0.8, 0.25, 1);\r\n$ease-elastic: cubic-bezier(0.34, 1.56, 0.64, 1);\r\n\r\n/* --- Main Container --- */\r\n.voice-status-wrapper {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 20px;\r\n padding: 24px;\r\n background: $bg-glass;\r\n backdrop-filter: blur(16px);\r\n -webkit-backdrop-filter: blur(16px);\r\n border: 1px solid $border-glass;\r\n border-radius: 24px;\r\n box-shadow:\r\n 0 10px 40px -10px rgba(0, 0, 0, 0.5),\r\n inset 0 1px 0 rgba(255, 255, 255, 0.1);\r\n transition: all 0.4s $ease-smooth;\r\n max-width: 480px;\r\n width: 100%;\r\n}\r\n\r\n/* --- Indicator Section --- */\r\n.indicator-container {\r\n position: relative;\r\n width: 64px;\r\n height: 64px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n}\r\n\r\n.ambient-glow {\r\n position: absolute;\r\n inset: -20px;\r\n border-radius: 50%;\r\n opacity: 0;\r\n filter: blur(20px);\r\n transition: all 0.5s ease;\r\n}\r\n\r\n.icon-core {\r\n position: relative;\r\n width: 52px;\r\n height: 52px;\r\n border-radius: 50%;\r\n background: rgba(255, 255, 255, 0.05);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border: 1px solid rgba(255, 255, 255, 0.1);\r\n z-index: 10;\r\n transition: all 0.4s $ease-elastic;\r\n overflow: hidden;\r\n\r\n .mic-icon {\r\n width: 24px;\r\n height: 24px;\r\n color: rgba(255, 255, 255, 0.9);\r\n transition: all 0.3s ease;\r\n\r\n .mic-capsule {\r\n transition: transform 0.2s ease;\r\n transform-origin: center center;\r\n }\r\n }\r\n}\r\n\r\n/* --- Ripple Effects --- */\r\n.ripple-layer {\r\n position: absolute;\r\n inset: 0;\r\n pointer-events: none;\r\n}\r\n\r\n.ripple {\r\n position: absolute;\r\n inset: 0;\r\n border-radius: 50%;\r\n border: 2px solid transparent;\r\n opacity: 0;\r\n}\r\n\r\n/* --- Content Section --- */\r\n.content-container {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0; /* Prevent flex text overflow */\r\n padding-top: 4px;\r\n}\r\n\r\n.status-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.status-dot {\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: currentColor;\r\n box-shadow: 0 0 8px currentColor;\r\n transition: color 0.3s ease;\r\n}\r\n\r\n.status-text {\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.05em;\r\n color: rgba(255, 255, 255, 0.6);\r\n}\r\n\r\n.text-window {\r\n min-height: 24px;\r\n position: relative;\r\n}\r\n\r\n.transcription-content {\r\n font-size: 16px;\r\n line-height: 1.5;\r\n color: #fff;\r\n font-weight: 500;\r\n margin: 0;\r\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);\r\n}\r\n\r\n.placeholder-text {\r\n font-size: 16px;\r\n color: rgba(255, 255, 255, 0.3);\r\n font-style: italic;\r\n margin: 0;\r\n}\r\n\r\n/* --- State: Wake --- */\r\n.mode-wake {\r\n border-color: rgba($c-wake, 0.3);\r\n\r\n .ambient-glow {\r\n background: $c-wake;\r\n opacity: 0.2;\r\n }\r\n\r\n .icon-core {\r\n background: linear-gradient(135deg, rgba($c-wake, 0.2), rgba($c-wake, 0.1));\r\n border-color: rgba($c-wake, 0.4);\r\n box-shadow: 0 0 15px rgba($c-wake, 0.2);\r\n\r\n .mic-icon {\r\n color: color.adjust($c-wake, $lightness: 10%);\r\n }\r\n }\r\n\r\n .status-dot {\r\n color: $c-wake;\r\n animation: pulse-dot 2s infinite;\r\n }\r\n\r\n .ripple {\r\n border-color: $c-wake;\r\n animation: ripple-out 3s infinite;\r\n\r\n &.delay-1 {\r\n animation-delay: 0s;\r\n }\r\n &.delay-2 {\r\n animation-delay: 1s;\r\n }\r\n &.delay-3 {\r\n animation-delay: 2s;\r\n }\r\n }\r\n}\r\n\r\n/* --- State: Transcribing --- */\r\n.mode-transcribing {\r\n border-color: rgba($c-transcribe, 0.3);\r\n\r\n .ambient-glow {\r\n background: $c-transcribe;\r\n opacity: 0.3;\r\n animation: glow-breathe 1.5s ease-in-out infinite alternate;\r\n }\r\n\r\n .icon-core {\r\n background: linear-gradient(135deg, rgba($c-transcribe, 0.3), rgba($c-transcribe, 0.1));\r\n border-color: rgba($c-transcribe, 0.5);\r\n box-shadow: 0 0 20px rgba($c-transcribe, 0.3);\r\n transform: scale(1.1);\r\n\r\n .mic-icon {\r\n color: #fff;\r\n\r\n .mic-capsule {\r\n animation: mic-bounce 0.6s ease-in-out infinite alternate;\r\n }\r\n }\r\n }\r\n\r\n .status-dot {\r\n color: $c-transcribe;\r\n }\r\n\r\n .ripple {\r\n border-color: $c-transcribe;\r\n animation: ripple-rapid 1.5s infinite;\r\n\r\n &.delay-1 {\r\n animation-delay: 0s;\r\n }\r\n &.delay-2 {\r\n animation-delay: 0.3s;\r\n }\r\n &.delay-3 {\r\n animation-delay: 0.6s;\r\n }\r\n }\r\n}\r\n\r\n/* --- Animations --- */\r\n@keyframes ripple-out {\r\n 0% {\r\n transform: scale(0.8);\r\n opacity: 0;\r\n border-width: 2px;\r\n }\r\n 20% {\r\n opacity: 0.5;\r\n }\r\n 100% {\r\n transform: scale(2.2);\r\n opacity: 0;\r\n border-width: 0px;\r\n }\r\n}\r\n\r\n@keyframes ripple-rapid {\r\n 0% {\r\n transform: scale(0.9);\r\n opacity: 0;\r\n }\r\n 50% {\r\n opacity: 0.4;\r\n }\r\n 100% {\r\n transform: scale(1.6);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n@keyframes pulse-dot {\r\n 0%,\r\n 100% {\r\n opacity: 1;\r\n transform: scale(1);\r\n }\r\n 50% {\r\n opacity: 0.5;\r\n transform: scale(0.8);\r\n }\r\n}\r\n\r\n@keyframes glow-breathe {\r\n 0% {\r\n opacity: 0.2;\r\n transform: scale(0.9);\r\n }\r\n 100% {\r\n opacity: 0.4;\r\n transform: scale(1.1);\r\n }\r\n}\r\n\r\n@keyframes mic-bounce {\r\n 0% {\r\n transform: scaleY(1);\r\n }\r\n 100% {\r\n transform: scaleY(0.7);\r\n }\r\n}\r\n\r\n/* --- Vue Transitions --- */\r\n.voice-panel-enter-active,\r\n.voice-panel-leave-active {\r\n transition: all 0.4s $ease-elastic;\r\n}\r\n.voice-panel-enter-from,\r\n.voice-panel-leave-to {\r\n opacity: 0;\r\n transform: translateY(20px) scale(0.95);\r\n}\r\n\r\n.fade-slide-enter-active,\r\n.fade-slide-leave-active {\r\n transition: all 0.3s ease;\r\n}\r\n.fade-slide-enter-from {\r\n opacity: 0;\r\n transform: translateY(10px);\r\n}\r\n.fade-slide-leave-to {\r\n opacity: 0;\r\n transform: translateY(-10px);\r\n}\r\n</style>\r\n","<template>\r\n <transition name=\"exec-bubble\">\r\n <div v-if=\"visible\" class=\"execution-bubble\">\r\n <!-- 文本内容 -->\r\n <span class=\"exec-text\">{{ text || '执行中' }}</span>\r\n <!-- 三点加载动画 -->\r\n <div class=\"loading-dots\">\r\n <span class=\"dot\"></span>\r\n <span class=\"dot\"></span>\r\n <span class=\"dot\"></span>\r\n </div>\r\n </div>\r\n </transition>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\ndefineProps<{\r\n visible: boolean;\r\n text?: string;\r\n}>();\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n$c-exec: #a855f7;\r\n$c-exec-light: #c084fc;\r\n$bg-glass: rgba(15, 23, 42, 0.9);\r\n$ease-smooth: cubic-bezier(0.25, 0.8, 0.25, 1);\r\n\r\n.execution-bubble {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 12px 20px;\r\n background: $bg-glass;\r\n backdrop-filter: blur(16px);\r\n -webkit-backdrop-filter: blur(16px);\r\n border: 1px solid rgba($c-exec, 0.35);\r\n border-radius: 20px;\r\n box-shadow:\r\n 0 4px 20px rgba(0, 0, 0, 0.3),\r\n 0 0 20px -5px rgba($c-exec, 0.3),\r\n inset 0 1px 0 rgba(255, 255, 255, 0.08);\r\n}\r\n\r\n/* 文本样式 */\r\n.exec-text {\r\n font-size: 14px;\r\n font-weight: 500;\r\n color: rgba(255, 255, 255, 0.9);\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n max-width: 300px;\r\n}\r\n\r\n/* 三点加载动画 */\r\n.loading-dots {\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n margin-left: 10px;\r\n}\r\n\r\n.dot {\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: $c-exec-light;\r\n animation: dot-bounce 1.4s ease-in-out infinite;\r\n\r\n &:nth-child(1) {\r\n animation-delay: 0s;\r\n }\r\n &:nth-child(2) {\r\n animation-delay: 0.2s;\r\n }\r\n &:nth-child(3) {\r\n animation-delay: 0.4s;\r\n }\r\n}\r\n\r\n@keyframes dot-bounce {\r\n 0%,\r\n 80%,\r\n 100% {\r\n transform: scale(0.6);\r\n opacity: 0.4;\r\n }\r\n 40% {\r\n transform: scale(1);\r\n opacity: 1;\r\n box-shadow: 0 0 8px rgba($c-exec-light, 0.6);\r\n }\r\n}\r\n\r\n/* Vue 过渡动画 */\r\n.exec-bubble-enter-active {\r\n transition: all 0.3s $ease-smooth;\r\n}\r\n\r\n.exec-bubble-leave-active {\r\n transition: all 0.2s $ease-smooth;\r\n}\r\n\r\n.exec-bubble-enter-from {\r\n opacity: 0;\r\n transform: scale(0.8);\r\n}\r\n\r\n.exec-bubble-leave-to {\r\n opacity: 0;\r\n transform: scale(0.8);\r\n}\r\n</style>\r\n","import { DiscoveredCommand } from '@siact/sime-bridge'\r\n\r\nexport const ensureMicrophonePermission = async () => {\r\n // 检查浏览器环境\r\n if (typeof navigator === 'undefined' || typeof window === 'undefined') {\r\n console.log('当前环境不支持麦克风访问');\r\n return false;\r\n }\r\n\r\n // 检查 MediaDevices API 支持\r\n if (!navigator.mediaDevices?.getUserMedia || !navigator.mediaDevices?.enumerateDevices) {\r\n console.log('当前环境不支持麦克风访问');\r\n return false;\r\n }\r\n\r\n try {\r\n // 1. 首先枚举设备,检查是否有音频输入设备\r\n const devices = await navigator.mediaDevices.enumerateDevices();\r\n const audioInputDevices = devices.filter((device) => device.kind === 'audioinput');\r\n\r\n if (audioInputDevices.length === 0) {\r\n console.log('未检测到麦克风设备,请连接麦克风后重试。');\r\n return false;\r\n }\r\n\r\n // 2. 检查权限状态(如果浏览器支持)\r\n if ('permissions' in navigator && navigator.permissions?.query) {\r\n try {\r\n const status = await navigator.permissions.query({ name: 'microphone' as PermissionName });\r\n\r\n if (status.state === 'denied') {\r\n console.log('麦克风权限被禁用,请在浏览器设置中开启。');\r\n return false;\r\n }\r\n } catch (e) {\r\n // 某些浏览器可能不支持 microphone 权限查询,继续执行\r\n console.warn('Permission query not supported:', e);\r\n }\r\n }\r\n\r\n // 3. 尝试获取麦克风流以确认权限和设备可用性\r\n let stream: MediaStream | null = null;\r\n try {\r\n stream = await navigator.mediaDevices.getUserMedia({\r\n audio: {\r\n echoCancellation: true,\r\n noiseSuppression: true,\r\n autoGainControl: true,\r\n },\r\n });\r\n\r\n // 检查流是否有活动的音频轨道\r\n const audioTracks = stream.getAudioTracks();\r\n if (audioTracks.length === 0) {\r\n console.log('无法获取麦克风音频轨道。');\r\n return false;\r\n }\r\n\r\n // 检查音频轨道是否启用且可读\r\n const activeTrack = audioTracks[0];\r\n if (!activeTrack.enabled || activeTrack.readyState !== 'live') {\r\n console.log('麦克风设备不可用,请检查设备连接。');\r\n return false;\r\n }\r\n\r\n return true;\r\n } finally {\r\n // 确保释放流资源\r\n if (stream) {\r\n stream.getTracks().forEach((track) => track.stop());\r\n }\r\n }\r\n } catch (error: any) {\r\n console.error('Microphone permission check failed', error);\r\n\r\n // 根据错误类型提供更具体的提示\r\n if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {\r\n console.log('未检测到麦克风设备,请连接麦克风后重试。');\r\n } else if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {\r\n console.log('麦克风权限被拒绝,请在浏览器设置中允许访问。');\r\n } else if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {\r\n console.log('麦克风被其他应用占用或无法访问。');\r\n } else {\r\n console.log('无法访问麦克风,请检查设备连接和浏览器权限。');\r\n }\r\n\r\n return false;\r\n }\r\n};\r\n\r\n\r\n/**\r\n * 语音命令匹配函数\r\n * @param {string} userInput - 用户输入的原始语音文本\r\n * @param {Array} commandList - 命令列表对象数组\r\n * @param {number} threshold - 相似度阈值 (0-1),默认 0.5\r\n */\r\nexport function matchVoiceCommand(userInput: string, commandList: DiscoveredCommand[], threshold = 0.5) {\r\n if (!userInput || typeof userInput !== 'string') return null;\r\n\r\n // 1. 预处理:转小写、去空格、去标点、去语气词\r\n const cleanInput = userInput\r\n .trim()\r\n .toLowerCase()\r\n // 1. 去除所有标点符号及特殊字符\r\n .replace(/[,。?!;:、“”()()<>《》\\s\\.\\,\\?\\!]/g, '')\r\n // 2. 去除常见的前缀 (增加“给我”、“能不能”等)\r\n .replace(/^(请帮我|我想|帮我|麻烦|我要|你好|您好|帮我把|给我|能不能帮我|请问|那个)/g, '')\r\n // 3. 去除中间的动作虚词 (如“把...给...”)\r\n .replace(/给(?=(打开|开启|关闭|停止))/g, '')\r\n // 4. 去除常见的后缀 (增加“就可以了”、“就行”、“模式”)\r\n .replace(/(一下|了|吧|输出|就可以了|就行|模式|操作|功能)$/g, '');\r\n\r\n let bestMatch = null;\r\n let maxScore = 0;\r\n\r\n // 编辑距离算法 (Levenshtein Distance)\r\n const getSimilarity = (s1: string, s2: string) => {\r\n const [l1, l2] = [s1.length, s2.length];\r\n const matrix = Array.from({ length: l1 + 1 }, () => Array(l2 + 1).fill(0));\r\n for (let i = 0; i <= l1; i++) matrix[i][0] = i;\r\n for (let j = 0; j <= l2; j++) matrix[0][j] = j;\r\n\r\n for (let i = 1; i <= l1; i++) {\r\n for (let j = 1; j <= l2; j++) {\r\n const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;\r\n matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);\r\n }\r\n }\r\n return 1 - matrix[l1][l2] / Math.max(l1, l2);\r\n };\r\n\r\n // 2. 遍历命令列表进行比对\r\n for (const cmd of commandList) {\r\n const target = cmd.description.toLowerCase();\r\n let currentScore = 0;\r\n\r\n // 策略 A:直接包含 (如输入“帮我开启漫游吧”,描述是“开启漫游”)\r\n if (cleanInput.includes(target) || target.includes(cleanInput)) {\r\n // 计算包含关系的得分,越接近 1 分越高\r\n currentScore = Math.min(target.length, cleanInput.length) / Math.max(target.length, cleanInput.length);\r\n // 给予包含匹配更高的基础权重\r\n currentScore = 0.8 + currentScore * 0.2;\r\n }\r\n // 策略 B:模糊匹配 (处理同音字错误,如“开启慢游”)\r\n else {\r\n currentScore = getSimilarity(cleanInput, target);\r\n }\r\n\r\n // 更新最高分项\r\n if (currentScore > maxScore) {\r\n maxScore = currentScore;\r\n bestMatch = { ...cmd, confidence: maxScore };\r\n }\r\n }\r\n\r\n // 3. 结果过滤\r\n return maxScore >= threshold ? bestMatch : null;\r\n}","<template>\r\n <div class=\"sime-x\" :data-theme=\"currentTheme\">\r\n <!-- 悬浮按钮(最小化状态) -->\r\n <transition name=\"fade\">\r\n <div ref=\"fabRef\" class=\"assistant-fab\" @click=\"toggleDialog(true)\">\r\n <VoiceStatus\r\n v-if=\"!isProcessing\"\r\n class=\"voice-status\"\r\n :status=\"voiceStatus\"\r\n :transcription-text=\"transcriptionText\"\r\n :is-transcribing=\"isTranscribing\"\r\n style=\"width: 480px\"\r\n />\r\n <ExecutionStatus v-else class=\"voice-status\" :visible=\"isProcessing\" :text=\"transcriptionText\" />\r\n <div class=\"fab-avatar-wrapper\">\r\n <img\r\n :src=\"xLogo ? xLogo : '/sime.png'\"\r\n alt=\"assistant\"\r\n :style=\"{\r\n width: xSize?.width + 'px',\r\n }\"\r\n />\r\n <!-- 优化后的监听状态指示器 -->\r\n <transition name=\"indicator-fade\">\r\n <div v-if=\"voiceStatus === 'listening'\" class=\"listening-badge\" :class=\"{ 'wake-active': wakeAnimating }\">\r\n <div class=\"listening-waves\">\r\n <div class=\"wave wave-1\"></div>\r\n <div class=\"wave wave-2\"></div>\r\n <div class=\"wave wave-3\"></div>\r\n </div>\r\n <div class=\"listening-icon\">\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path\r\n d=\"M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z\"\r\n fill=\"currentColor\"\r\n />\r\n <path\r\n d=\"M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n />\r\n </svg>\r\n </div>\r\n </div>\r\n </transition>\r\n </div>\r\n <div class=\"fab-pulse\" :class=\"{ active: voiceStatus === 'listening' }\"></div>\r\n </div>\r\n </transition>\r\n\r\n <!-- 弹窗 -->\r\n <transition name=\"dialog-fade\">\r\n <div\r\n ref=\"dialogRef\"\r\n class=\"x-dialog-container\"\r\n :class=\"{\r\n collapsed: isCollapsed,\r\n 'is-hidden': !visible,\r\n 'position-ready': positionReady,\r\n }\"\r\n :style=\"{\r\n width: containerWidth + 'px',\r\n height: isCollapsed ? 'auto' : containerHeight + 'px',\r\n border: currentTheme === 'light' && !isCollapsed ? '1px solid var(--border-color)' : 'none',\r\n '--dialog-x': position.x + 'px',\r\n '--dialog-y': position.y + 'px',\r\n }\"\r\n @mousedown=\"startDrag\"\r\n >\r\n <div class=\"x-dialog-header\" @mousedown.stop=\"startDrag\">\r\n <div class=\"header-left\">\r\n <div class=\"logo-icon\">\r\n <img :src=\"xLogo ? xLogo : '/sime.png'\" alt=\"assistant\" class=\"logo\" />\r\n </div>\r\n <span class=\"title\">{{ xTitle }}</span>\r\n </div>\r\n <div class=\"actions\">\r\n <button class=\"action-btn theme-btn\" title=\"开启新对话\" @click=\"startNewConversation\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M12 5v14M5 12h14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\r\n </svg>\r\n </button>\r\n <button\r\n class=\"action-btn theme-btn\"\r\n :class=\"{\r\n active: voiceStatus !== 'standby',\r\n listening: voiceStatus === 'listening',\r\n woke: voiceStatus === 'wake',\r\n }\"\r\n @click.stop=\"() => toggleVoiceMode()\"\r\n :title=\"voiceButtonTooltip\"\r\n >\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path\r\n d=\"M12 15C13.6569 15 15 13.6569 15 12V7C15 5.34315 13.6569 4 12 4C10.3431 4 9 5.34315 9 7V12C9 13.6569 10.3431 15 12 15Z\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path\r\n d=\"M18 11C18 14.3137 15.3137 17 12 17C8.68629 17 6 14.3137 6 11\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path\r\n d=\"M12 19V17\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path\r\n d=\"M9 21H15\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n <span class=\"voice-indicator\" v-if=\"voiceStatus !== 'standby'\"></span>\r\n </button>\r\n <button class=\"action-btn theme-btn\" @click.stop=\"cycleTheme\" :title=\"themeTooltip\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <circle cx=\"12\" cy=\"12\" r=\"7\" stroke=\"currentColor\" stroke-width=\"2\" />\r\n <path d=\"M12 5A7 7 0 1 1 12 19\" fill=\"currentColor\" />\r\n </svg>\r\n </button>\r\n <button class=\"action-btn collapse-btn\" @click.stop=\"toggleCollapse\" :title=\"isCollapsed ? '展开' : '折叠'\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path\r\n :d=\"isCollapsed ? 'M18 15L12 9L6 15' : 'M6 9L12 15L18 9'\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n </button>\r\n <button class=\"action-btn minimize-btn\" @click.stop=\"toggleDialog(false)\" title=\"最小化\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M5 12H19\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n <div :class=\"['x-dialog-content', { 'is-hidden': isCollapsed }]\" :style=\"{ opacity: isCollapsed ? 0 : 1 }\">\r\n <iframe\r\n ref=\"iframeRef\"\r\n :src=\"chatbotUrl\"\r\n class=\"x-iframe\"\r\n allow=\"microphone\"\r\n frameborder=\"0\"\r\n @load=\"handleIframeLoad\"\r\n ></iframe>\r\n </div>\r\n </div>\r\n </transition>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, reactive, onBeforeUnmount, computed, nextTick, watch } from 'vue';\r\nimport VoiceStatus from './voice-status.vue';\r\nimport ExecutionStatus from './execution-status.vue';\r\nimport { injectStrict, AiChatbotXKey } from '../injection-key';\r\nimport { WakeWordDetectorStandalone, SpeechTranscriberStandalone } from 'web-voice-kit';\r\nimport { ensureMicrophonePermission, matchVoiceCommand } from '../lib/utils';\r\n\r\ninterface XSize {\r\n width: number;\r\n height: number;\r\n}\r\ninterface xDialogSize {\r\n width: number;\r\n height: number;\r\n}\r\n\r\nconst props = defineProps<{\r\n xLogo?: string;\r\n xSize?: XSize;\r\n xTitle?: string;\r\n xTheme?: 'light' | 'dark' | 'system';\r\n xDialogSize?: xDialogSize;\r\n wakeWords?: string[];\r\n modelPath?: string;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n (e: 'start-transcribing'): void;\r\n (e: 'stop-transcribing'): void;\r\n (e: 'wakeUp', isWake: boolean): void;\r\n}>();\r\n\r\nconst aiChatbotX = injectStrict(AiChatbotXKey);\r\n\r\nconst chatbotUrl = ref('');\r\nconst voiceStatus = ref<'wake' | 'listening' | 'standby'>('standby');\r\nconst wakeAnimating = ref(false);\r\nconst wakeResponses = ['在呢', '在的', '我在', '您好', '在呢,请说'];\r\nconst transcriptionText = ref<string>('');\r\nconst isTranscribing = ref(false);\r\nconst isProcessing = ref(false);\r\nconst visible = ref(false);\r\nconst isCollapsed = ref(false);\r\nconst fabRef = ref<HTMLElement | null>(null);\r\nconst positionReady = ref(false);\r\n\r\nlet detector: WakeWordDetectorStandalone | null = null;\r\nlet transcriber: SpeechTranscriberStandalone | null = null;\r\n\r\nconst isInitializing = ref(false);\r\nconst initError = ref<string>('');\r\n\r\n// 获取系统主题\r\nconst getSystemTheme = (): 'light' | 'dark' => {\r\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\r\n};\r\nconst currentTheme = ref<'light' | 'dark'>(props.xTheme === 'system' ? getSystemTheme() : props.xTheme || 'light');\r\n\r\n// 切换主题\r\nconst cycleTheme = async () => {\r\n currentTheme.value = currentTheme.value === 'light' ? 'dark' : 'light';\r\n aiChatbotX.setTheme(currentTheme.value);\r\n};\r\n\r\n// 开启新对话\r\nconst startNewConversation = () => {\r\n aiChatbotX.startNewConversation();\r\n};\r\n\r\nconst themeTooltip = computed(() => (currentTheme.value === 'light' ? '切换到深色模式' : '切换到浅色模式'));\r\n\r\nconst voiceButtonTooltip = computed(() => {\r\n if (isInitializing.value) return '初始化中...';\r\n if (initError.value) return `错误: ${initError.value}`;\r\n\r\n switch (voiceStatus.value) {\r\n case 'standby':\r\n return '开启语音监听';\r\n case 'listening':\r\n return '监听中,等待唤醒词...';\r\n case 'wake':\r\n return '已唤醒!';\r\n default:\r\n return '语音监听';\r\n }\r\n});\r\n\r\nif (props.xTheme === 'system') {\r\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n const handleThemeChange = (e: MediaQueryListEvent) => {\r\n currentTheme.value = e.matches ? 'dark' : 'light';\r\n };\r\n mediaQuery.addEventListener('change', handleThemeChange);\r\n onBeforeUnmount(() => {\r\n mediaQuery.removeEventListener('change', handleThemeChange);\r\n });\r\n}\r\n\r\n// 初始化语音监听器\r\nconst initVoiceDetector = () => {\r\n if (detector || isInitializing.value) return;\r\n\r\n isInitializing.value = true;\r\n initError.value = '';\r\n\r\n if (!props.modelPath) {\r\n initError.value = '未提供语音模型文件';\r\n isInitializing.value = false;\r\n return;\r\n }\r\n\r\n try {\r\n detector = new WakeWordDetectorStandalone({\r\n modelPath: props.modelPath,\r\n sampleRate: 16000,\r\n usePartial: true,\r\n autoReset: {\r\n enabled: true,\r\n resetDelayMs: 5000,\r\n },\r\n });\r\n\r\n const wakeWords = props.wakeWords || ['你好', '您好'];\r\n detector.setWakeWords(wakeWords);\r\n\r\n detector.onWake(() => {\r\n console.log('[VoiceDetector] 检测到唤醒词');\r\n // 触发唤醒动画\r\n wakeAnimating.value = true;\r\n // 语音播报回应\r\n playWakeResponse();\r\n // 通知 web 端\r\n emit('wakeUp', true);\r\n aiChatbotX.weak();\r\n aiChatbotX.openDialog();\r\n // 动画结束后恢复\r\n setTimeout(() => {\r\n wakeAnimating.value = false;\r\n }, 1500);\r\n // 保持 listening 状态,继续后台唤醒监听\r\n voiceStatus.value = 'listening';\r\n });\r\n\r\n detector.onError((error: any) => {\r\n console.error('[VoiceDetector] 错误:', error);\r\n initError.value = error.message;\r\n voiceStatus.value = 'standby';\r\n isTranscribing.value = false;\r\n\r\n if (error.message.includes('permission')) {\r\n transcriptionText.value = '需要麦克风权限';\r\n } else if (error.message.includes('model')) {\r\n transcriptionText.value = '模型加载失败';\r\n } else {\r\n transcriptionText.value = '初始化失败';\r\n }\r\n\r\n setTimeout(() => {\r\n transcriptionText.value = '';\r\n }, 3000);\r\n });\r\n\r\n console.log('[VoiceDetector] 初始化成功');\r\n } catch (error) {\r\n console.error('[VoiceDetector] 初始化失败:', error);\r\n initError.value = error instanceof Error ? error.message : '初始化失败';\r\n voiceStatus.value = 'standby';\r\n isTranscribing.value = false;\r\n } finally {\r\n isInitializing.value = false;\r\n }\r\n};\r\n\r\n// 语音播报唤醒回应(使用 Web Speech Synthesis API,无需音频文件)\r\nconst playWakeResponse = () => {\r\n try {\r\n if (typeof window === 'undefined' || !window.speechSynthesis) {\r\n console.warn('[TTS] SpeechSynthesis API 不可用');\r\n return;\r\n }\r\n const text = wakeResponses[Math.floor(Math.random() * wakeResponses.length)];\r\n const utterance = new SpeechSynthesisUtterance(text);\r\n utterance.lang = 'zh-CN';\r\n utterance.rate = 1.0;\r\n utterance.pitch = 0.8;\r\n utterance.volume = 0.9;\r\n // 优先选择中文男声\r\n const voices = window.speechSynthesis.getVoices();\r\n const maleVoice = voices.find((v) => v.lang.startsWith('zh') && /male|男/i.test(v.name));\r\n const zhVoice = maleVoice || voices.find((v) => v.lang.startsWith('zh'));\r\n if (zhVoice) {\r\n utterance.voice = zhVoice;\r\n }\r\n window.speechSynthesis.speak(utterance);\r\n } catch (error) {\r\n console.error('[TTS] 播报失败:', error);\r\n }\r\n};\r\n\r\n// 初始化转写器\r\nfunction initTranscriber() {\r\n if (transcriber) return;\r\n\r\n try {\r\n const { appId, apiKey, websocketUrl } = aiChatbotX.voiceConfig();\r\n if (!appId || !apiKey || !websocketUrl) {\r\n initError.value = '未配置语音配置';\r\n voiceStatus.value = 'standby';\r\n isTranscribing.value = false;\r\n return;\r\n }\r\n transcriber = new SpeechTranscriberStandalone({\r\n appId,\r\n apiKey,\r\n websocketUrl,\r\n autoStop: {\r\n enabled: true,\r\n silenceTimeoutMs: 3000,\r\n noSpeechTimeoutMs: 5000,\r\n maxDurationMs: 60000,\r\n },\r\n });\r\n\r\n transcriber.onResult((result) => {\r\n transcriptionText.value = result.transcript;\r\n });\r\n\r\n transcriber.onAutoStop(async () => {\r\n console.log('[Transcriber] Auto Stop');\r\n\r\n // 保存当前转写文本,用于后续处理\r\n const currentText = transcriptionText.value;\r\n\r\n // 停止转写\r\n await stopTranscribing();\r\n\r\n // 如果没有转写内容,直接返回后台监听\r\n if (!currentText || !currentText.trim()) {\r\n console.log('[Transcriber] No transcription text, returning to listening');\r\n transcriptionText.value = '';\r\n voiceStatus.value = 'listening';\r\n return;\r\n }\r\n\r\n // 进入处理中状态,显示优雅的等待动画\r\n isProcessing.value = true;\r\n transcriptionText.value = currentText; // 保持显示用户说的内容\r\n\r\n try {\r\n const commands = await aiChatbotX.hostCommads();\r\n const result = await aiChatbotX.recognition(currentText, commands);\r\n\r\n // 如果是命令类型,执行匹配的命令\r\n if (result?.data?.intent === 'command' && result?.data?.matchedCommands) {\r\n const matchedCommands = result.data.matchedCommands;\r\n\r\n for (const cmd of matchedCommands) {\r\n try {\r\n // 将参数对象转换为参数数组\r\n const args = cmd.parameters ? Object.values(cmd.parameters) : [];\r\n\r\n // 执行宿主命令\r\n const cmdResult = await aiChatbotX.executeCommand(cmd.name, args);\r\n console.log(`Command ${cmd.name} executed successfully:`, cmdResult);\r\n } catch (error) {\r\n console.error(`Failed to execute command ${cmd.name}:`, error);\r\n }\r\n }\r\n } else {\r\n aiChatbotX.appendMessage(currentText);\r\n toggleDialog(true);\r\n }\r\n } finally {\r\n // 处理完成,返回后台监听状态\r\n isProcessing.value = false;\r\n transcriptionText.value = '';\r\n voiceStatus.value = 'listening';\r\n }\r\n });\r\n\r\n transcriber.onError((error) => {\r\n console.error('[Transcriber] Error:', error);\r\n stopTranscribing();\r\n transcriptionText.value = '转写错误';\r\n setTimeout(() => {\r\n transcriptionText.value = '';\r\n voiceStatus.value = 'listening';\r\n }, 2000);\r\n });\r\n\r\n console.log('[Transcriber] 初始化成功');\r\n } catch (error) {\r\n console.error('[Transcriber] 初始化失败:', error);\r\n voiceStatus.value = 'standby';\r\n initError.value = error instanceof Error ? error.message : '转写初始化失败';\r\n }\r\n}\r\n\r\nconst startTranscribing = async () => {\r\n if (!transcriber) {\r\n initTranscriber();\r\n if (!transcriber) return;\r\n }\r\n\r\n try {\r\n emit('start-transcribing');\r\n await transcriber.start();\r\n isTranscribing.value = true;\r\n transcriptionText.value = '';\r\n } catch (error) {\r\n console.error('[Transcriber] 启动失败:', error);\r\n transcriptionText.value = '转写启动失败';\r\n setTimeout(() => {\r\n transcriptionText.value = '';\r\n }, 2000);\r\n }\r\n};\r\n\r\nconst stopTranscribing = async () => {\r\n if (transcriber && transcriber.isActive()) {\r\n try {\r\n await transcriber.stop();\r\n isTranscribing.value = false;\r\n emit('stop-transcribing');\r\n } catch (error) {\r\n console.error('[Transcriber] 停止失败:', error);\r\n }\r\n }\r\n};\r\n\r\nconst toggleVoiceMode = async (targetState?: boolean) => {\r\n const permission = await ensureMicrophonePermission();\r\n if (!permission) return;\r\n\r\n if (isInitializing.value) return;\r\n\r\n if (!detector) {\r\n await initVoiceDetector();\r\n if (!detector) return;\r\n }\r\n\r\n // 核心逻辑:如果传了参数,目标就是参数值;如果不传,目标就是当前状态的反值\r\n // 假设 \"listening\" 为开启状态,其余(如 \"standby\")为关闭状态\r\n const isCurrentlyListening = voiceStatus.value === 'listening';\r\n const shouldStart = targetState !== undefined ? targetState : !isCurrentlyListening;\r\n\r\n // 如果当前状态已经符合目标状态,直接返回(防止重复开启或关闭)\r\n if (shouldStart === isCurrentlyListening) return;\r\n\r\n try {\r\n if (shouldStart) {\r\n // 执行开启逻辑\r\n console.log('[VoiceDetector] 强制/自动启动监听...');\r\n await detector.start();\r\n voiceStatus.value = 'listening';\r\n transcriptionText.value = '';\r\n isTranscribing.value = false;\r\n } else {\r\n // 执行关闭逻辑\r\n console.log('[VoiceDetector] 强制/自动停止监听...');\r\n await detector.stop();\r\n stopTranscribing();\r\n voiceStatus.value = 'standby';\r\n transcriptionText.value = '';\r\n isTranscribing.value = false;\r\n }\r\n } catch (error) {\r\n console.error('[VoiceDetector] 操作失败:', error);\r\n voiceStatus.value = 'standby';\r\n transcriptionText.value = '操作失败';\r\n isTranscribing.value = false;\r\n\r\n setTimeout(() => {\r\n transcriptionText.value = '';\r\n }, 2000);\r\n }\r\n};\r\n\r\nconst position = reactive({ x: 0, y: 0 });\r\nconst containerWidth = ref(props.xDialogSize?.width || 420);\r\nconst containerHeight = ref(props.xDialogSize?.height || 600);\r\nconst isFirstOpen = ref(true);\r\n\r\nconst validatePosition = (x: number, y: number, width: number, height: number) => {\r\n const margin = 20;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n\r\n const maxX = viewportWidth - width - margin;\r\n const maxY = viewportHeight - height - margin;\r\n\r\n return {\r\n x: Math.max(margin, Math.min(x, maxX)),\r\n y: Math.max(margin, Math.min(y, maxY)),\r\n };\r\n};\r\n\r\nconst calculateInitialPosition = () => {\r\n if (!fabRef.value) return;\r\n\r\n const fabRect = fabRef.value.getBoundingClientRect();\r\n const dialogWidth = containerWidth.value;\r\n const dialogHeight = containerHeight.value;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n const margin = 20; // 增加间距,确保不遮挡\r\n const minMargin = 20; // 最小边距\r\n\r\n let x = 0;\r\n let y = 0;\r\n\r\n // 优先尝试在 FAB 左侧定位\r\n const leftX = fabRect.left - dialogWidth - margin;\r\n\r\n if (leftX >= minMargin) {\r\n // 左侧有足够空间\r\n x = leftX;\r\n // Y 轴对齐 FAB 顶部,但确保不超出视口\r\n y = fabRect.top;\r\n\r\n // 如果对话框底部超出视口,向上调整\r\n if (y + dialogHeight > viewportHeight - minMargin) {\r\n y = viewportHeight - dialogHeight - minMargin;\r\n }\r\n // 确保顶部不超出\r\n if (y < minMargin) {\r\n y = minMargin;\r\n }\r\n } else {\r\n // 左侧空间不足,尝试右侧\r\n const rightX = fabRect.right + margin;\r\n\r\n if (rightX + dialogWidth <= viewportWidth - minMargin) {\r\n // 右侧有足够空间\r\n x = rightX;\r\n y = fabRect.top;\r\n\r\n // Y 轴调整\r\n if (y + dialogHeight > viewportHeight - minMargin) {\r\n y = viewportHeight - dialogHeight - minMargin;\r\n }\r\n if (y < minMargin) {\r\n y = minMargin;\r\n }\r\n } else {\r\n // 左右都不够,居中显示,并确保不遮挡 FAB\r\n x = (viewportWidth - dialogWidth) / 2;\r\n\r\n // 尝试在 FAB 上方\r\n const aboveY = fabRect.top - dialogHeight - margin;\r\n if (aboveY >= minMargin) {\r\n y = aboveY;\r\n } else {\r\n // 在 FAB 下方\r\n const belowY = fabRect.bottom + margin;\r\n if (belowY + dialogHeight <= viewportHeight - minMargin) {\r\n y = belowY;\r\n } else {\r\n // 垂直居中\r\n y = (viewportHeight - dialogHeight) / 2;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 最终验证位置\r\n const validated = validatePosition(x, y, dialogWidth, dialogHeight);\r\n position.x = validated.x;\r\n position.y = validated.y;\r\n};\r\n\r\nconst toggleCollapse = async () => {\r\n isCollapsed.value = !isCollapsed.value;\r\n\r\n nextTick(() => {\r\n const currentHeight = isCollapsed.value ? 60 : containerHeight.value;\r\n const validated = validatePosition(position.x, position.y, containerWidth.value, currentHeight);\r\n position.x = validated.x;\r\n position.y = validated.y;\r\n });\r\n};\r\n\r\nconst drag = reactive({\r\n isDragging: false,\r\n startX: 0,\r\n startY: 0,\r\n offsetX: 0,\r\n offsetY: 0,\r\n});\r\n\r\nconst startDrag = (e: MouseEvent) => {\r\n drag.isDragging = true;\r\n drag.startX = e.clientX;\r\n drag.startY = e.clientY;\r\n drag.offsetX = position.x;\r\n drag.offsetY = position.y;\r\n document.addEventListener('mousemove', onDrag);\r\n document.addEventListener('mouseup', stopDrag);\r\n};\r\n\r\nconst onDrag = (e: MouseEvent) => {\r\n if (!drag.isDragging) return;\r\n\r\n const newX = drag.offsetX + (e.clientX - drag.startX);\r\n const newY = drag.offsetY + (e.clientY - drag.startY);\r\n\r\n const validated = validatePosition(newX, newY, containerWidth.value, isCollapsed.value ? 60 : containerHeight.value);\r\n\r\n position.x = validated.x;\r\n position.y = validated.y;\r\n};\r\n\r\nconst stopDrag = () => {\r\n drag.isDragging = false;\r\n document.removeEventListener('mousemove', onDrag);\r\n document.removeEventListener('mouseup', stopDrag);\r\n};\r\n\r\nconst toggleDialog = async (state: boolean) => {\r\n if (state) {\r\n visible.value = true;\r\n positionReady.value = false;\r\n\r\n await nextTick();\r\n\r\n if (isFirstOpen.value) {\r\n calculateInitialPosition();\r\n isFirstOpen.value = false;\r\n } else {\r\n const validated = validatePosition(\r\n position.x,\r\n position.y,\r\n containerWidth.value,\r\n isCollapsed.value ? 60 : containerHeight.value,\r\n );\r\n position.x = validated.x;\r\n position.y = validated.y;\r\n }\r\n\r\n await nextTick();\r\n positionReady.value = true;\r\n } else {\r\n positionReady.value = false;\r\n visible.value = false;\r\n isCollapsed.value = false;\r\n emit('wakeUp', false);\r\n }\r\n};\r\n\r\nconst handleIframeLoad = (event: Event) => {\r\n aiChatbotX.setIframeElement(event.target as HTMLIFrameElement);\r\n aiChatbotX.setTheme('dark');\r\n};\r\n\r\nwatch(\r\n () => [aiChatbotX.chatbotUrl()],\r\n ([url]) => {\r\n console.log('[AiChatbotX] 初始化', url);\r\n if (url) {\r\n chatbotUrl.value = `${url}/app/${aiChatbotX.appId()}?token=${aiChatbotX.appToken()}`;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\nonBeforeUnmount(async () => {\r\n if (detector) {\r\n try {\r\n if (detector.isActive()) {\r\n await detector.stop();\r\n }\r\n detector = null;\r\n } catch (error) {\r\n console.error('[VoiceDetector] 清理失败:', error);\r\n }\r\n }\r\n if (transcriber) {\r\n try {\r\n if (transcriber.isActive()) {\r\n await transcriber.stop();\r\n }\r\n transcriber = null;\r\n } catch (error) {\r\n console.error('[Transcriber] 清理失败:', error);\r\n }\r\n }\r\n});\r\n\r\naiChatbotX?.registerVoiceMethods({\r\n start: () => toggleVoiceMode(true),\r\n stop: () => toggleVoiceMode(false),\r\n openDialog: () => toggleDialog(true),\r\n closeDialog: () => toggleDialog(false),\r\n toggleCollapse: () => toggleCollapse(),\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.sime-x {\r\n --bg-primary: #ffffff;\r\n --bg-secondary: #f8fafc;\r\n --bg-header: #0f172a;\r\n --text-primary: #0f172a;\r\n --text-secondary: #64748b;\r\n --text-header: #ffffff;\r\n --border-color: #e2e8f0;\r\n --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);\r\n --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);\r\n --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.15);\r\n --accent-color: #3b82f6;\r\n --hover-bg: rgba(0, 0, 0, 0.05);\r\n\r\n &[data-theme='dark'] {\r\n --bg-primary: #1e293b;\r\n --bg-secondary: #0f172a;\r\n --bg-header: #0f172a;\r\n --text-primary: #f1f5f9;\r\n --text-secondary: #94a3b8;\r\n --text-header: #f1f5f9;\r\n --border-color: #334155;\r\n --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);\r\n --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);\r\n --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.5);\r\n --accent-color: #60a5fa;\r\n --hover-bg: rgba(255, 255, 255, 0.1);\r\n }\r\n}\r\n\r\n.is-hidden {\r\n height: 0;\r\n opacity: 0 !important;\r\n pointer-events: none !important;\r\n visibility: hidden !important;\r\n}\r\n\r\n.assistant-fab {\r\n position: relative;\r\n cursor: pointer;\r\n overflow: visible;\r\n filter: drop-shadow(var(--shadow-md));\r\n opacity: 1;\r\n pointer-events: auto;\r\n transition: all 0.3s ease;\r\n\r\n &:hover {\r\n transform: scale(1.05);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n\r\n .voice-status {\r\n position: absolute;\r\n right: 100%;\r\n margin-right: 20px;\r\n }\r\n\r\n img {\r\n display: block;\r\n position: relative;\r\n object-fit: contain;\r\n }\r\n\r\n .fab-pulse {\r\n position: absolute;\r\n inset: -4px;\r\n border-radius: 50%;\r\n border: 2px solid var(--accent-color);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n@keyframes pulse {\r\n 0%,\r\n 100% {\r\n opacity: 0;\r\n transform: scale(1);\r\n }\r\n 50% {\r\n opacity: 0.5;\r\n transform: scale(1.1);\r\n }\r\n}\r\n\r\n.x-dialog-container {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n transform: translate(var(--dialog-x), var(--dialog-y));\r\n border-radius: 16px;\r\n display: flex;\r\n flex-direction: column;\r\n cursor: grab;\r\n overflow: hidden;\r\n z-index: 1100;\r\n opacity: 0;\r\n pointer-events: none;\r\n\r\n &.position-ready {\r\n opacity: 1;\r\n pointer-events: auto;\r\n }\r\n\r\n &:active {\r\n cursor: grabbing;\r\n }\r\n\r\n &.collapsed {\r\n .x-dialog-header {\r\n border-radius: 16px;\r\n }\r\n }\r\n\r\n .x-dialog-header {\r\n position: relative;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n background: var(--bg-header);\r\n padding: 12px 16px;\r\n cursor: move;\r\n user-select: none;\r\n border-bottom: 1px solid var(--border-color);\r\n\r\n .header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n\r\n .logo-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 36px;\r\n height: 36px;\r\n transition: transform 0.2s ease;\r\n\r\n .logo {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: contain;\r\n }\r\n }\r\n\r\n .title {\r\n font-weight: 600;\r\n font-size: 15px;\r\n color: var(--text-header);\r\n letter-spacing: -0.01em;\r\n }\r\n }\r\n\r\n .actions {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n\r\n .action-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border: none;\r\n background: transparent;\r\n cursor: pointer;\r\n color: var(--text-secondary);\r\n transition: all 0.2s ease;\r\n border-radius: 8px;\r\n\r\n &:hover {\r\n color: var(--text-header);\r\n background: var(--hover-bg);\r\n transform: translateY(-1px);\r\n }\r\n\r\n &:active {\r\n transform: translateY(0);\r\n }\r\n\r\n svg {\r\n transition: transform 0.2s ease;\r\n }\r\n\r\n &.collapse-btn:hover svg {\r\n transform: scale(1.1);\r\n }\r\n }\r\n }\r\n }\r\n\r\n .voice-container {\r\n width: 100%;\r\n opacity: 1;\r\n pointer-events: auto;\r\n }\r\n\r\n .x-dialog-content {\r\n flex: 1;\r\n display: flex;\r\n overflow: hidden;\r\n background: var(--bg-secondary);\r\n opacity: 1;\r\n pointer-events: auto;\r\n }\r\n\r\n .x-iframe {\r\n flex: 1;\r\n width: 100%;\r\n height: 100%;\r\n border: none;\r\n }\r\n\r\n .resize-handle {\r\n position: absolute;\r\n bottom: 0;\r\n right: 0;\r\n width: 40px;\r\n height: 40px;\r\n cursor: nwse-resize;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--text-secondary);\r\n transition: all 0.2s ease;\r\n background: linear-gradient(135deg, transparent 50%, var(--bg-primary) 50%);\r\n border-bottom-right-radius: 16px;\r\n z-index: 10;\r\n\r\n &:hover {\r\n color: var(--accent-color);\r\n background: linear-gradient(135deg, transparent 50%, var(--hover-bg) 50%);\r\n }\r\n\r\n svg {\r\n width: 16px;\r\n height: 16px;\r\n transform: rotate(0deg);\r\n transition: transform 0.2s ease;\r\n }\r\n\r\n &:hover svg {\r\n transform: scale(1.2);\r\n }\r\n }\r\n}\r\n/* 语音按钮状态样式 */\r\n.action-btn.theme-btn {\r\n position: relative;\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.action-btn.theme-btn.active {\r\n background: rgba(59, 130, 246, 0.1);\r\n color: #3b82f6;\r\n}\r\n\r\n.action-btn.theme-btn.listening {\r\n animation: pulse-listening 2s ease-in-out infinite;\r\n}\r\n\r\n.action-btn.theme-btn.woke {\r\n background: rgba(34, 197, 94, 0.2);\r\n color: #22c55e;\r\n animation: pulse-wake 0.6s ease-in-out;\r\n}\r\n\r\n@keyframes pulse-listening {\r\n 0%,\r\n 100% {\r\n box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);\r\n }\r\n 50% {\r\n box-shadow: 0 0 0 8px rgba(59, 130, 246, 0);\r\n }\r\n}\r\n\r\n@keyframes pulse-wake {\r\n 0%,\r\n 100% {\r\n transform: scale(1);\r\n }\r\n 50% {\r\n transform: scale(1.1);\r\n }\r\n}\r\n\r\n/* 语音指示器 */\r\n.voice-indicator {\r\n position: absolute;\r\n top: 4px;\r\n right: 4px;\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: #3b82f6;\r\n}\r\n\r\n.action-btn.theme-btn.listening .voice-indicator {\r\n animation: blink 1.5s ease-in-out infinite;\r\n}\r\n\r\n.action-btn.theme-btn.woke .voice-indicator {\r\n background: #22c55e;\r\n animation: none;\r\n}\r\n\r\n@keyframes blink {\r\n 0%,\r\n 100% {\r\n opacity: 1;\r\n }\r\n 50% {\r\n opacity: 0.3;\r\n }\r\n}\r\n\r\n/* 麦克风唤醒动画 */\r\n.listening-badge.wake-active {\r\n background-color: rgba(34, 197, 94, 0.85);\r\n animation: wake-badge-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;\r\n\r\n .listening-icon {\r\n animation: wake-icon-flash 1.5s ease-in-out;\r\n }\r\n\r\n &::after {\r\n content: '';\r\n position: absolute;\r\n inset: -3px;\r\n border-radius: 50%;\r\n border: 2px solid #22c55e;\r\n animation: wake-mic-ring 1.2s ease-out forwards;\r\n pointer-events: none;\r\n }\r\n}\r\n\r\n@keyframes wake-badge-pop {\r\n 0% {\r\n transform: scale(1);\r\n }\r\n 50% {\r\n transform: scale(1.4);\r\n }\r\n 100% {\r\n transform: scale(1);\r\n }\r\n}\r\n\r\n@keyframes wake-icon-flash {\r\n 0%,\r\n 100% {\r\n color: #ffffff;\r\n }\r\n 25% {\r\n color: #bbf7d0;\r\n }\r\n 50% {\r\n color: #ffffff;\r\n }\r\n 75% {\r\n color: #bbf7d0;\r\n }\r\n}\r\n\r\n@keyframes wake-mic-ring {\r\n 0% {\r\n transform: scale(1);\r\n opacity: 0.8;\r\n }\r\n 100% {\r\n transform: scale(2.2);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n/* FAB 头像容器 */\r\n.fab-avatar-wrapper {\r\n position: relative;\r\n display: flex; /* 改用 flex 可以消除图片底部的行高间隙 */\r\n align-items: center;\r\n justify-content: center;\r\n width: fit-content; /* 宽度随内容自适应 */\r\n margin: 0 auto;\r\n}\r\n\r\n/* 2. 修正图片样式 */\r\n.fab-avatar-wrapper img {\r\n display: block;\r\n max-width: 100%;\r\n height: auto;\r\n /* 移除原本 style 里的 width/height 可能造成的拉伸,保持比例 */\r\n}\r\n\r\n/* 3. 优化徽章定位 */\r\n.listening-badge {\r\n position: absolute;\r\n /* 强制定位到容器边缘 */\r\n top: 0;\r\n right: 0;\r\n /* 使用 transform 偏移,让徽章中心点落在图片的右上角顶点上 */\r\n transform: translate(30%, -30%);\r\n\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background-color: rgba(0, 0, 0, 0.75); /* 稍微加深一点 */\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 10;\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\r\n\r\n /* 继承你原有的动画 */\r\n animation: badge-float 5s ease-in-out infinite;\r\n pointer-events: none; /* 防止遮挡头像点击 */\r\n}\r\n\r\n/* 4. 调整声波扩散的基准 */\r\n.wave {\r\n position: absolute;\r\n /* 确保声波从徽章中心扩散,而不是从容器左上角 */\r\n inset: -2px;\r\n border-radius: 50%;\r\n border: 2px solid rgba(59, 130, 246, 0.6); /* 声波颜色建议跟主题色一致 */\r\n animation: wave-expand 3s ease-out infinite;\r\n}\r\n\r\n/* 优化后的监听状态徽章 */\r\n.listening-badge {\r\n position: absolute;\r\n top: -8px;\r\n right: -8px;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background-color: rgba(0, 0, 0, 0.651);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 10;\r\n animation: badge-float 5s ease-in-out infinite;\r\n}\r\n\r\n/* 徽章浮动动画 */\r\n@keyframes badge-float {\r\n 0%,\r\n 100% {\r\n transform: translateY(0) scale(1);\r\n }\r\n 50% {\r\n transform: translateY(-2px) scale(1.05);\r\n }\r\n}\r\n\r\n/* 声波容器 */\r\n.listening-waves {\r\n position: absolute;\r\n inset: 0;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n/* 声波动画 */\r\n.wave {\r\n position: absolute;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n border: 2px solid rgba(255, 255, 255, 0.6);\r\n animation: wave-expand 3s ease-out infinite;\r\n}\r\n\r\n.wave-1 {\r\n animation-delay: 0s;\r\n}\r\n\r\n.wave-2 {\r\n animation-delay: 0.6s;\r\n}\r\n\r\n.wave-3 {\r\n animation-delay: 1.2s;\r\n}\r\n\r\n@keyframes wave-expand {\r\n 0% {\r\n transform: scale(0.8);\r\n opacity: 0.8;\r\n }\r\n 100% {\r\n transform: scale(1.2);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n/* 麦克风图标 */\r\n.listening-icon {\r\n position: relative;\r\n width: 24px;\r\n height: 24px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #ffffff;\r\n z-index: 1;\r\n animation: icon-pulse 2.5s ease-in-out infinite;\r\n}\r\n\r\n@keyframes icon-pulse {\r\n 0%,\r\n 100% {\r\n transform: scale(1);\r\n }\r\n 50% {\r\n transform: scale(1.1);\r\n }\r\n}\r\n\r\n/* 徽章淡入淡出动画 */\r\n.indicator-fade-enter-active {\r\n transition: all 1s cubic-bezier(0.34, 1.56, 0.64, 1);\r\n}\r\n\r\n.indicator-fade-leave-active {\r\n transition: all 1s ease;\r\n}\r\n\r\n.indicator-fade-enter-from {\r\n opacity: 0;\r\n transform: scale(0.3) rotate(-180deg);\r\n}\r\n\r\n.indicator-fade-leave-to {\r\n opacity: 0;\r\n transform: scale(0.5) rotate(180deg);\r\n}\r\n\r\n/* FAB 脉冲效果 */\r\n.fab-pulse {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n border-radius: 50%;\r\n pointer-events: none;\r\n}\r\n\r\n.fab-pulse.active {\r\n animation: fab-pulse 2s ease-in-out infinite;\r\n}\r\n\r\n@keyframes fab-pulse {\r\n 0%,\r\n 100% {\r\n box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);\r\n }\r\n 50% {\r\n box-shadow: 0 0 0 20px rgba(59, 130, 246, 0);\r\n }\r\n}\r\n\r\n/* 深色模式适配 */\r\n[data-theme='dark'] .action-btn.theme-btn.active {\r\n background: rgba(59, 130, 246, 0.2);\r\n}\r\n\r\n[data-theme='dark'] .action-btn.theme-btn.woke {\r\n background: rgba(34, 197, 94, 0.3);\r\n}\r\n</style>\r\n"],"names":["clientCommandKey","_renderSlot","_createBlock","_Transition","_createElementBlock","_normalizeClass","_createElementVNode","_hoisted_1","_hoisted_2","_hoisted_3","_toDisplayString","_createVNode","_hoisted_4","_hoisted_5","_openBlock","_normalizeStyle"],"mappings":"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"}
1
+ {"version":3,"file":"sime-x-vue.mjs","sources":["../src/injection-key.ts","../src/types.ts","../src/components/sime-provider.vue","../src/components/voice-status.vue","../src/components/execution-status.vue","../src/lib/utils.ts","../src/components/sime-x.vue"],"sourcesContent":["import type { InjectionKey } from 'vue'\r\nimport type { AiChatbotXContext } from './types'\r\nimport { inject } from 'vue'\r\n\r\nexport const AiChatbotXKey: InjectionKey<AiChatbotXContext> = Symbol('sime-x')\r\n\r\nexport function injectStrict<T>(key: InjectionKey<T>): T\r\nexport function injectStrict<T>(key: InjectionKey<T>, defaultValue: T, treatDefaultAsFactory?: false): T\r\nexport function injectStrict<T>(key: InjectionKey<T>, defaultValue: T | (() => T), treatDefaultAsFactory: true): T\r\nexport function injectStrict<T>(key: InjectionKey<T>, defaultValue?: T | (() => T), treatDefaultAsFactory?: boolean): T {\r\n let result: T | undefined\r\n\r\n if (defaultValue === undefined) {\r\n result = inject(key) as T | undefined\r\n }\r\n else if (treatDefaultAsFactory === true) {\r\n result = inject(key, defaultValue as T | (() => T), true)\r\n }\r\n else {\r\n result = inject(key, defaultValue as T, false)\r\n }\r\n\r\n if (!result) {\r\n throw new Error(`Could not resolve ${key.description}`)\r\n }\r\n return result\r\n}\r\n","import type { CommandDefinition, DiscoveredCommand } from \"@siact/sime-bridge\";\r\n\r\nexport enum clientCommandKey {\r\n SET_THEME = \"SiMeAgent_setTheme\",\r\n APPEND_MESSAGE = \"SiMeAgent_appendMessage\",\r\n WAKE = \"SiMeAgent_wake\",\r\n TRANSITION = \"SiMeAgent_transition\",\r\n TRANSITION_END = \"SiMeAgent_transition_end\",\r\n START_NEW_CONVERSATION = \"SiMeAgent_startNewConversation\",\r\n RECOGNITION = 'SiMeAgent_recognition'\r\n}\r\n\r\nexport interface VoiceConfig {\r\n appId: string;\r\n apiKey: string;\r\n websocketUrl: string;\r\n}\r\n\r\nexport interface AiChatbotXContext {\r\n chatbotUrl: () => string;\r\n appId: () => string;\r\n appToken: () => string;\r\n voiceConfig: () => VoiceConfig;\r\n registerCommand: (cmd: CommandDefinition) => void;\r\n unregisterCommand: (name: string) => void;\r\n clientCommand: () => Promise<DiscoveredCommand[]>;\r\n hostCommads: () => Promise<DiscoveredCommand[]>;\r\n appendMessage: (message: string) => Promise<void>;\r\n setTheme: (theme: string) => Promise<void>;\r\n weak: () => Promise<void>;\r\n startListening: () => Promise<void>;\r\n stopListening: () => Promise<void>;\r\n toggleCollapse: () => Promise<void>;\r\n openDialog: () => Promise<void>;\r\n closeDialog: () => Promise<void>;\r\n registerVoiceMethods: (methods: {\r\n start: () => Promise<void>;\r\n stop: () => Promise<void>;\r\n toggleCollapse: () => Promise<void>;\r\n openDialog: () => Promise<void>;\r\n closeDialog: () => Promise<void>;\r\n }) => void;\r\n startNewConversation: () => Promise<void>;\r\n setIframeElement: (iframe: HTMLIFrameElement) => void;\r\n recognition: (message: string, commands: DiscoveredCommand[]) => Promise<any>;\r\n executeCommand: (commandName: string, args?: any[]) => Promise<any>;\r\n}\r\n","<template>\r\n <slot></slot>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { provide, shallowRef } from 'vue';\r\nimport { HostBridge, CommandDefinition } from '@siact/sime-bridge';\r\nimport { AiChatbotXKey } from '../injection-key';\r\nimport { clientCommandKey, VoiceConfig } from '../types';\r\n\r\nconst props = defineProps<{\r\n /** 项目名称 */\r\n project: string;\r\n /** 项目描述 */\r\n description?: string;\r\n /** 是否开启调试模式 */\r\n debug?: boolean;\r\n /** iframeUrl */\r\n chatbotUrl: string;\r\n /** appId */\r\n appId: string;\r\n /** appToken */\r\n appToken: string;\r\n /** 语音配置 */\r\n voiceConfig: VoiceConfig;\r\n}>();\r\n\r\nconst hostBridge = shallowRef<HostBridge>(new HostBridge());\r\n\r\nconst startListeningRef = shallowRef<() => Promise<void>>(async () => {});\r\nconst stopListeningRef = shallowRef<() => Promise<void>>(async () => {});\r\nconst toggleCollapseRef = shallowRef<() => Promise<void>>(async () => {});\r\nconst openDialogRef = shallowRef<() => Promise<void>>(async () => {});\r\nconst closeDialogRef = shallowRef<() => Promise<void>>(async () => {});\r\n\r\nprovide(AiChatbotXKey, {\r\n chatbotUrl: () => props.chatbotUrl,\r\n appId: () => props.appId,\r\n appToken: () => props.appToken,\r\n voiceConfig: () => props.voiceConfig,\r\n startListening: () => startListeningRef.value(),\r\n stopListening: () => stopListeningRef.value(),\r\n toggleCollapse: () => toggleCollapseRef.value(),\r\n openDialog: () => openDialogRef.value(),\r\n closeDialog: () => closeDialogRef.value(),\r\n registerVoiceMethods: (methods: {\r\n start: () => Promise<void>;\r\n stop: () => Promise<void>;\r\n toggleCollapse: () => Promise<void>;\r\n openDialog: () => Promise<void>;\r\n closeDialog: () => Promise<void>;\r\n }) => {\r\n startListeningRef.value = methods.start;\r\n stopListeningRef.value = methods.stop;\r\n toggleCollapseRef.value = methods.toggleCollapse;\r\n openDialogRef.value = methods.openDialog;\r\n closeDialogRef.value = methods.closeDialog;\r\n },\r\n clientCommand: () => hostBridge.value.clientCommands(),\r\n hostCommads: () => hostBridge.value.hostCommands(),\r\n registerCommand: (cmd: CommandDefinition) => {\r\n hostBridge.value.registerCommand(cmd);\r\n },\r\n unregisterCommand: (name) => {\r\n hostBridge.value.unregisterCommand(name);\r\n },\r\n async appendMessage(message) {\r\n await hostBridge.value.executeClientCommand(clientCommandKey.APPEND_MESSAGE, [message]);\r\n },\r\n async setTheme(theme) {\r\n await hostBridge.value.executeClientCommand(clientCommandKey.SET_THEME, [theme]);\r\n },\r\n async weak() {\r\n await hostBridge.value.executeClientCommand(clientCommandKey.WAKE);\r\n },\r\n async startNewConversation() {\r\n await hostBridge.value.executeClientCommand(clientCommandKey.START_NEW_CONVERSATION);\r\n },\r\n setIframeElement(iframe) {\r\n hostBridge.value.setIframe(iframe);\r\n },\r\n async recognition(message, commands) {\r\n return await hostBridge.value.executeClientCommand(clientCommandKey.RECOGNITION, [message, commands]);\r\n },\r\n async executeCommand(commandName, args = []) {\r\n return await hostBridge.value.executeCommand(commandName, args);\r\n },\r\n});\r\n</script>\r\n","<template>\r\n <transition name=\"voice-panel\">\r\n <!-- Main Container -->\r\n <div v-if=\"status === 'wake' || isTranscribing\" class=\"voice-status-wrapper\" :class=\"currentMode\">\r\n <!-- Visual Indicator Section -->\r\n <div class=\"indicator-container\">\r\n <!-- Ambient Glow (Background) -->\r\n <div class=\"ambient-glow\"></div>\r\n\r\n <!-- Animated Ripples -->\r\n <div class=\"ripple-layer\">\r\n <div class=\"ripple delay-1\"></div>\r\n <div class=\"ripple delay-2\"></div>\r\n <div class=\"ripple delay-3\"></div>\r\n </div>\r\n\r\n <!-- Core Icon Circle -->\r\n <div class=\"icon-core\">\r\n <!-- Unified Microphone Icon -->\r\n <svg\r\n class=\"mic-icon\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <!-- Microphone Capsule (Animates on voice) -->\r\n <rect x=\"9\" y=\"2\" width=\"6\" height=\"12\" rx=\"3\" class=\"mic-capsule\" />\r\n <!-- Stand/Base -->\r\n <path d=\"M5 10C5 13.866 8.13401 17 12 17C15.866 17 19 13.866 19 10\" class=\"mic-stand\" />\r\n <path d=\"M12 17V21M8 21H16\" class=\"mic-base\" />\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n <!-- Content Section -->\r\n <div class=\"content-container\">\r\n <!-- Status Header -->\r\n <div class=\"status-header\">\r\n <span class=\"status-text\">{{ statusLabel }}</span>\r\n </div>\r\n\r\n <!-- Transcription Text (Animated Entry) -->\r\n <div class=\"text-window\" :class=\"{ 'has-text': !!transcriptionText }\">\r\n <transition name=\"fade-slide\" mode=\"out-in\">\r\n <p v-if=\"transcriptionText\" class=\"transcription-content\">\r\n {{ transcriptionText }}\r\n </p>\r\n <p v-else-if=\"status === 'wake'\" class=\"placeholder-text\">Listening...</p>\r\n </transition>\r\n </div>\r\n </div>\r\n </div>\r\n </transition>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from 'vue';\r\ntype VoiceStatus = 'wake' | 'listening' | 'standby';\r\n\r\nconst props = defineProps<{\r\n status: VoiceStatus;\r\n transcriptionText?: string;\r\n isTranscribing?: boolean;\r\n}>();\r\n\r\n// Determine the current visual mode\r\nconst currentMode = computed(() => {\r\n if (props.isTranscribing) return 'mode-transcribing';\r\n if (props.status === 'wake') return 'mode-wake';\r\n return 'mode-standby';\r\n});\r\n\r\n// Dynamic label text\r\nconst statusLabel = computed(() => {\r\n if (props.isTranscribing) return '正在聆听您的问题...';\r\n if (props.status === 'wake') return '您好,有什么可以帮助您的吗?';\r\n return 'Standby';\r\n});\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n@use 'sass:color';\r\n\r\n/* --- Design Tokens --- */\r\n$c-wake: #10b981; // Emerald for Wake\r\n$c-transcribe: #3b82f6; // Blue for Transcribing\r\n$bg-glass: rgba(15, 23, 42, 0.85);\r\n$border-glass: rgba(255, 255, 255, 0.1);\r\n$ease-smooth: cubic-bezier(0.25, 0.8, 0.25, 1);\r\n$ease-elastic: cubic-bezier(0.34, 1.56, 0.64, 1);\r\n\r\n/* --- Main Container --- */\r\n.voice-status-wrapper {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 20px;\r\n padding: 24px;\r\n background: $bg-glass;\r\n backdrop-filter: blur(16px);\r\n -webkit-backdrop-filter: blur(16px);\r\n border: 1px solid $border-glass;\r\n border-radius: 24px;\r\n box-shadow:\r\n 0 10px 40px -10px rgba(0, 0, 0, 0.5),\r\n inset 0 1px 0 rgba(255, 255, 255, 0.1);\r\n transition: all 0.4s $ease-smooth;\r\n max-width: 480px;\r\n width: 100%;\r\n}\r\n\r\n/* --- Indicator Section --- */\r\n.indicator-container {\r\n position: relative;\r\n width: 64px;\r\n height: 64px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n}\r\n\r\n.ambient-glow {\r\n position: absolute;\r\n inset: -20px;\r\n border-radius: 50%;\r\n opacity: 0;\r\n filter: blur(20px);\r\n transition: all 0.5s ease;\r\n}\r\n\r\n.icon-core {\r\n position: relative;\r\n width: 52px;\r\n height: 52px;\r\n border-radius: 50%;\r\n background: rgba(255, 255, 255, 0.05);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border: 1px solid rgba(255, 255, 255, 0.1);\r\n z-index: 10;\r\n transition: all 0.4s $ease-elastic;\r\n overflow: hidden;\r\n\r\n .mic-icon {\r\n width: 24px;\r\n height: 24px;\r\n color: rgba(255, 255, 255, 0.9);\r\n transition: all 0.3s ease;\r\n\r\n .mic-capsule {\r\n transition: transform 0.2s ease;\r\n transform-origin: center center;\r\n }\r\n }\r\n}\r\n\r\n/* --- Ripple Effects --- */\r\n.ripple-layer {\r\n position: absolute;\r\n inset: 0;\r\n pointer-events: none;\r\n}\r\n\r\n.ripple {\r\n position: absolute;\r\n inset: 0;\r\n border-radius: 50%;\r\n border: 2px solid transparent;\r\n opacity: 0;\r\n}\r\n\r\n/* --- Content Section --- */\r\n.content-container {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0; /* Prevent flex text overflow */\r\n padding-top: 4px;\r\n}\r\n\r\n.status-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.status-dot {\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: currentColor;\r\n box-shadow: 0 0 8px currentColor;\r\n transition: color 0.3s ease;\r\n}\r\n\r\n.status-text {\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.05em;\r\n color: rgba(255, 255, 255, 0.6);\r\n}\r\n\r\n.text-window {\r\n min-height: 24px;\r\n position: relative;\r\n}\r\n\r\n.transcription-content {\r\n font-size: 16px;\r\n line-height: 1.5;\r\n color: #fff;\r\n font-weight: 500;\r\n margin: 0;\r\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);\r\n}\r\n\r\n.placeholder-text {\r\n font-size: 16px;\r\n color: rgba(255, 255, 255, 0.3);\r\n font-style: italic;\r\n margin: 0;\r\n}\r\n\r\n/* --- State: Wake --- */\r\n.mode-wake {\r\n border-color: rgba($c-wake, 0.3);\r\n\r\n .ambient-glow {\r\n background: $c-wake;\r\n opacity: 0.2;\r\n }\r\n\r\n .icon-core {\r\n background: linear-gradient(135deg, rgba($c-wake, 0.2), rgba($c-wake, 0.1));\r\n border-color: rgba($c-wake, 0.4);\r\n box-shadow: 0 0 15px rgba($c-wake, 0.2);\r\n\r\n .mic-icon {\r\n color: color.adjust($c-wake, $lightness: 10%);\r\n }\r\n }\r\n\r\n .status-dot {\r\n color: $c-wake;\r\n animation: pulse-dot 2s infinite;\r\n }\r\n\r\n .ripple {\r\n border-color: $c-wake;\r\n animation: ripple-out 3s infinite;\r\n\r\n &.delay-1 {\r\n animation-delay: 0s;\r\n }\r\n &.delay-2 {\r\n animation-delay: 1s;\r\n }\r\n &.delay-3 {\r\n animation-delay: 2s;\r\n }\r\n }\r\n}\r\n\r\n/* --- State: Transcribing --- */\r\n.mode-transcribing {\r\n border-color: rgba($c-transcribe, 0.3);\r\n\r\n .ambient-glow {\r\n background: $c-transcribe;\r\n opacity: 0.3;\r\n animation: glow-breathe 1.5s ease-in-out infinite alternate;\r\n }\r\n\r\n .icon-core {\r\n background: linear-gradient(135deg, rgba($c-transcribe, 0.3), rgba($c-transcribe, 0.1));\r\n border-color: rgba($c-transcribe, 0.5);\r\n box-shadow: 0 0 20px rgba($c-transcribe, 0.3);\r\n transform: scale(1.1);\r\n\r\n .mic-icon {\r\n color: #fff;\r\n\r\n .mic-capsule {\r\n animation: mic-bounce 0.6s ease-in-out infinite alternate;\r\n }\r\n }\r\n }\r\n\r\n .status-dot {\r\n color: $c-transcribe;\r\n }\r\n\r\n .ripple {\r\n border-color: $c-transcribe;\r\n animation: ripple-rapid 1.5s infinite;\r\n\r\n &.delay-1 {\r\n animation-delay: 0s;\r\n }\r\n &.delay-2 {\r\n animation-delay: 0.3s;\r\n }\r\n &.delay-3 {\r\n animation-delay: 0.6s;\r\n }\r\n }\r\n}\r\n\r\n/* --- Animations --- */\r\n@keyframes ripple-out {\r\n 0% {\r\n transform: scale(0.8);\r\n opacity: 0;\r\n border-width: 2px;\r\n }\r\n 20% {\r\n opacity: 0.5;\r\n }\r\n 100% {\r\n transform: scale(2.2);\r\n opacity: 0;\r\n border-width: 0px;\r\n }\r\n}\r\n\r\n@keyframes ripple-rapid {\r\n 0% {\r\n transform: scale(0.9);\r\n opacity: 0;\r\n }\r\n 50% {\r\n opacity: 0.4;\r\n }\r\n 100% {\r\n transform: scale(1.6);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n@keyframes pulse-dot {\r\n 0%,\r\n 100% {\r\n opacity: 1;\r\n transform: scale(1);\r\n }\r\n 50% {\r\n opacity: 0.5;\r\n transform: scale(0.8);\r\n }\r\n}\r\n\r\n@keyframes glow-breathe {\r\n 0% {\r\n opacity: 0.2;\r\n transform: scale(0.9);\r\n }\r\n 100% {\r\n opacity: 0.4;\r\n transform: scale(1.1);\r\n }\r\n}\r\n\r\n@keyframes mic-bounce {\r\n 0% {\r\n transform: scaleY(1);\r\n }\r\n 100% {\r\n transform: scaleY(0.7);\r\n }\r\n}\r\n\r\n/* --- Vue Transitions --- */\r\n.voice-panel-enter-active,\r\n.voice-panel-leave-active {\r\n transition: all 0.4s $ease-elastic;\r\n}\r\n.voice-panel-enter-from,\r\n.voice-panel-leave-to {\r\n opacity: 0;\r\n transform: translateY(20px) scale(0.95);\r\n}\r\n\r\n.fade-slide-enter-active,\r\n.fade-slide-leave-active {\r\n transition: all 0.3s ease;\r\n}\r\n.fade-slide-enter-from {\r\n opacity: 0;\r\n transform: translateY(10px);\r\n}\r\n.fade-slide-leave-to {\r\n opacity: 0;\r\n transform: translateY(-10px);\r\n}\r\n</style>\r\n","<template>\r\n <transition name=\"exec-bubble\">\r\n <div v-if=\"visible\" class=\"execution-bubble\">\r\n <!-- 文本内容 -->\r\n <span class=\"exec-text\">{{ text || '执行中' }}</span>\r\n <!-- 三点加载动画 -->\r\n <div class=\"loading-dots\">\r\n <span class=\"dot\"></span>\r\n <span class=\"dot\"></span>\r\n <span class=\"dot\"></span>\r\n </div>\r\n </div>\r\n </transition>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\ndefineProps<{\r\n visible: boolean;\r\n text?: string;\r\n}>();\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n$c-exec: #a855f7;\r\n$c-exec-light: #c084fc;\r\n$bg-glass: rgba(15, 23, 42, 0.9);\r\n$ease-smooth: cubic-bezier(0.25, 0.8, 0.25, 1);\r\n\r\n.execution-bubble {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 12px 20px;\r\n background: $bg-glass;\r\n backdrop-filter: blur(16px);\r\n -webkit-backdrop-filter: blur(16px);\r\n border: 1px solid rgba($c-exec, 0.35);\r\n border-radius: 20px;\r\n box-shadow:\r\n 0 4px 20px rgba(0, 0, 0, 0.3),\r\n 0 0 20px -5px rgba($c-exec, 0.3),\r\n inset 0 1px 0 rgba(255, 255, 255, 0.08);\r\n}\r\n\r\n/* 文本样式 */\r\n.exec-text {\r\n font-size: 14px;\r\n font-weight: 500;\r\n color: rgba(255, 255, 255, 0.9);\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n max-width: 300px;\r\n}\r\n\r\n/* 三点加载动画 */\r\n.loading-dots {\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n margin-left: 10px;\r\n}\r\n\r\n.dot {\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: $c-exec-light;\r\n animation: dot-bounce 1.4s ease-in-out infinite;\r\n\r\n &:nth-child(1) {\r\n animation-delay: 0s;\r\n }\r\n &:nth-child(2) {\r\n animation-delay: 0.2s;\r\n }\r\n &:nth-child(3) {\r\n animation-delay: 0.4s;\r\n }\r\n}\r\n\r\n@keyframes dot-bounce {\r\n 0%,\r\n 80%,\r\n 100% {\r\n transform: scale(0.6);\r\n opacity: 0.4;\r\n }\r\n 40% {\r\n transform: scale(1);\r\n opacity: 1;\r\n box-shadow: 0 0 8px rgba($c-exec-light, 0.6);\r\n }\r\n}\r\n\r\n/* Vue 过渡动画 */\r\n.exec-bubble-enter-active {\r\n transition: all 0.3s $ease-smooth;\r\n}\r\n\r\n.exec-bubble-leave-active {\r\n transition: all 0.2s $ease-smooth;\r\n}\r\n\r\n.exec-bubble-enter-from {\r\n opacity: 0;\r\n transform: scale(0.8);\r\n}\r\n\r\n.exec-bubble-leave-to {\r\n opacity: 0;\r\n transform: scale(0.8);\r\n}\r\n</style>\r\n","import { DiscoveredCommand } from '@siact/sime-bridge'\r\n\r\nexport const ensureMicrophonePermission = async () => {\r\n // 检查浏览器环境\r\n if (typeof navigator === 'undefined' || typeof window === 'undefined') {\r\n console.log('当前环境不支持麦克风访问');\r\n return false;\r\n }\r\n\r\n // 检查 MediaDevices API 支持\r\n if (!navigator.mediaDevices?.getUserMedia || !navigator.mediaDevices?.enumerateDevices) {\r\n console.log('当前环境不支持麦克风访问');\r\n return false;\r\n }\r\n\r\n try {\r\n // 1. 首先枚举设备,检查是否有音频输入设备\r\n const devices = await navigator.mediaDevices.enumerateDevices();\r\n const audioInputDevices = devices.filter((device) => device.kind === 'audioinput');\r\n\r\n if (audioInputDevices.length === 0) {\r\n console.log('未检测到麦克风设备,请连接麦克风后重试。');\r\n return false;\r\n }\r\n\r\n // 2. 检查权限状态(如果浏览器支持)\r\n if ('permissions' in navigator && navigator.permissions?.query) {\r\n try {\r\n const status = await navigator.permissions.query({ name: 'microphone' as PermissionName });\r\n\r\n if (status.state === 'denied') {\r\n console.log('麦克风权限被禁用,请在浏览器设置中开启。');\r\n return false;\r\n }\r\n } catch (e) {\r\n // 某些浏览器可能不支持 microphone 权限查询,继续执行\r\n console.warn('Permission query not supported:', e);\r\n }\r\n }\r\n\r\n // 3. 尝试获取麦克风流以确认权限和设备可用性\r\n let stream: MediaStream | null = null;\r\n try {\r\n stream = await navigator.mediaDevices.getUserMedia({\r\n audio: {\r\n echoCancellation: true,\r\n noiseSuppression: true,\r\n autoGainControl: true,\r\n },\r\n });\r\n\r\n // 检查流是否有活动的音频轨道\r\n const audioTracks = stream.getAudioTracks();\r\n if (audioTracks.length === 0) {\r\n console.log('无法获取麦克风音频轨道。');\r\n return false;\r\n }\r\n\r\n // 检查音频轨道是否启用且可读\r\n const activeTrack = audioTracks[0];\r\n if (!activeTrack.enabled || activeTrack.readyState !== 'live') {\r\n console.log('麦克风设备不可用,请检查设备连接。');\r\n return false;\r\n }\r\n\r\n return true;\r\n } finally {\r\n // 确保释放流资源\r\n if (stream) {\r\n stream.getTracks().forEach((track) => track.stop());\r\n }\r\n }\r\n } catch (error: any) {\r\n console.error('Microphone permission check failed', error);\r\n\r\n // 根据错误类型提供更具体的提示\r\n if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {\r\n console.log('未检测到麦克风设备,请连接麦克风后重试。');\r\n } else if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {\r\n console.log('麦克风权限被拒绝,请在浏览器设置中允许访问。');\r\n } else if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {\r\n console.log('麦克风被其他应用占用或无法访问。');\r\n } else {\r\n console.log('无法访问麦克风,请检查设备连接和浏览器权限。');\r\n }\r\n\r\n return false;\r\n }\r\n};\r\n\r\n\r\n/**\r\n * 语音命令匹配函数\r\n * @param {string} userInput - 用户输入的原始语音文本\r\n * @param {Array} commandList - 命令列表对象数组\r\n * @param {number} threshold - 相似度阈值 (0-1),默认 0.5\r\n */\r\nexport function matchVoiceCommand(userInput: string, commandList: DiscoveredCommand[], threshold = 0.5) {\r\n if (!userInput || typeof userInput !== 'string') return null;\r\n\r\n // 1. 预处理:转小写、去空格、去标点、去语气词\r\n const cleanInput = userInput\r\n .trim()\r\n .toLowerCase()\r\n // 1. 去除所有标点符号及特殊字符\r\n .replace(/[,。?!;:、“”()()<>《》\\s\\.\\,\\?\\!]/g, '')\r\n // 2. 去除常见的前缀 (增加“给我”、“能不能”等)\r\n .replace(/^(请帮我|我想|帮我|麻烦|我要|你好|您好|帮我把|给我|能不能帮我|请问|那个)/g, '')\r\n // 3. 去除中间的动作虚词 (如“把...给...”)\r\n .replace(/给(?=(打开|开启|关闭|停止))/g, '')\r\n // 4. 去除常见的后缀 (增加“就可以了”、“就行”、“模式”)\r\n .replace(/(一下|了|吧|输出|就可以了|就行|模式|操作|功能)$/g, '');\r\n\r\n let bestMatch = null;\r\n let maxScore = 0;\r\n\r\n // 编辑距离算法 (Levenshtein Distance)\r\n const getSimilarity = (s1: string, s2: string) => {\r\n const [l1, l2] = [s1.length, s2.length];\r\n const matrix = Array.from({ length: l1 + 1 }, () => Array(l2 + 1).fill(0));\r\n for (let i = 0; i <= l1; i++) matrix[i][0] = i;\r\n for (let j = 0; j <= l2; j++) matrix[0][j] = j;\r\n\r\n for (let i = 1; i <= l1; i++) {\r\n for (let j = 1; j <= l2; j++) {\r\n const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;\r\n matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);\r\n }\r\n }\r\n return 1 - matrix[l1][l2] / Math.max(l1, l2);\r\n };\r\n\r\n // 2. 遍历命令列表进行比对\r\n for (const cmd of commandList) {\r\n const target = cmd.description.toLowerCase();\r\n let currentScore = 0;\r\n\r\n // 策略 A:直接包含 (如输入“帮我开启漫游吧”,描述是“开启漫游”)\r\n if (cleanInput.includes(target) || target.includes(cleanInput)) {\r\n // 计算包含关系的得分,越接近 1 分越高\r\n currentScore = Math.min(target.length, cleanInput.length) / Math.max(target.length, cleanInput.length);\r\n // 给予包含匹配更高的基础权重\r\n currentScore = 0.8 + currentScore * 0.2;\r\n }\r\n // 策略 B:模糊匹配 (处理同音字错误,如“开启慢游”)\r\n else {\r\n currentScore = getSimilarity(cleanInput, target);\r\n }\r\n\r\n // 更新最高分项\r\n if (currentScore > maxScore) {\r\n maxScore = currentScore;\r\n bestMatch = { ...cmd, confidence: maxScore };\r\n }\r\n }\r\n\r\n // 3. 结果过滤\r\n return maxScore >= threshold ? bestMatch : null;\r\n}","<template>\r\n <div class=\"sime-x\" :data-theme=\"currentTheme\">\r\n <!-- 悬浮按钮(最小化状态) -->\r\n <transition name=\"fade\">\r\n <div ref=\"fabRef\" class=\"assistant-fab\" @click=\"toggleDialog(!visible)\">\r\n <VoiceStatus\r\n v-if=\"!isProcessing\"\r\n class=\"voice-status\"\r\n :status=\"voiceStatus\"\r\n :transcription-text=\"transcriptionText\"\r\n :is-transcribing=\"isTranscribing\"\r\n style=\"width: 480px\"\r\n />\r\n <ExecutionStatus v-else class=\"voice-status\" :visible=\"isProcessing\" :text=\"transcriptionText\" />\r\n <div class=\"fab-avatar-wrapper\">\r\n <img\r\n :src=\"xLogo ? xLogo : '/sime.png'\"\r\n alt=\"assistant\"\r\n :style=\"{\r\n width: xSize?.width + 'px',\r\n }\"\r\n />\r\n <!-- 优化后的监听状态指示器 -->\r\n <transition name=\"indicator-fade\">\r\n <div v-if=\"voiceStatus === 'listening'\" class=\"listening-badge\" :class=\"{ 'wake-active': wakeAnimating }\">\r\n <div class=\"listening-waves\">\r\n <div class=\"wave wave-1\"></div>\r\n <div class=\"wave wave-2\"></div>\r\n <div class=\"wave wave-3\"></div>\r\n </div>\r\n <div class=\"listening-icon\">\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path\r\n d=\"M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z\"\r\n fill=\"currentColor\"\r\n />\r\n <path\r\n d=\"M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n />\r\n </svg>\r\n </div>\r\n </div>\r\n </transition>\r\n </div>\r\n <div class=\"fab-pulse\" :class=\"{ active: voiceStatus === 'listening' }\"></div>\r\n </div>\r\n </transition>\r\n\r\n <!-- 弹窗 -->\r\n <transition name=\"dialog-fade\">\r\n <div\r\n ref=\"dialogRef\"\r\n class=\"x-dialog-container\"\r\n :class=\"{\r\n collapsed: isCollapsed,\r\n 'is-hidden': !visible,\r\n 'position-ready': positionReady,\r\n }\"\r\n :style=\"{\r\n width: containerWidth + 'px',\r\n height: isCollapsed ? 'auto' : containerHeight + 'px',\r\n border: currentTheme === 'light' && !isCollapsed ? '1px solid var(--border-color)' : 'none',\r\n '--dialog-x': position.x + 'px',\r\n '--dialog-y': position.y + 'px',\r\n }\"\r\n @mousedown=\"startDrag\"\r\n >\r\n <div class=\"x-dialog-header\" @mousedown.stop=\"startDrag\">\r\n <div class=\"header-left\">\r\n <div class=\"logo-icon\">\r\n <img :src=\"xLogo ? xLogo : '/sime.png'\" alt=\"assistant\" class=\"logo\" />\r\n </div>\r\n <span class=\"title\">{{ xTitle }}</span>\r\n </div>\r\n <div class=\"actions\">\r\n <button class=\"action-btn theme-btn\" title=\"开启新对话\" @click=\"startNewConversation\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M12 5v14M5 12h14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\r\n </svg>\r\n </button>\r\n <button\r\n class=\"action-btn theme-btn\"\r\n :class=\"{\r\n active: voiceStatus !== 'standby',\r\n listening: voiceStatus === 'listening',\r\n woke: voiceStatus === 'wake',\r\n }\"\r\n @click.stop=\"() => toggleVoiceMode()\"\r\n :title=\"voiceButtonTooltip\"\r\n >\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path\r\n d=\"M12 15C13.6569 15 15 13.6569 15 12V7C15 5.34315 13.6569 4 12 4C10.3431 4 9 5.34315 9 7V12C9 13.6569 10.3431 15 12 15Z\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path\r\n d=\"M18 11C18 14.3137 15.3137 17 12 17C8.68629 17 6 14.3137 6 11\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path\r\n d=\"M12 19V17\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path\r\n d=\"M9 21H15\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n <span class=\"voice-indicator\" v-if=\"voiceStatus !== 'standby'\"></span>\r\n </button>\r\n <button class=\"action-btn theme-btn\" @click.stop=\"cycleTheme\" :title=\"themeTooltip\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <circle cx=\"12\" cy=\"12\" r=\"7\" stroke=\"currentColor\" stroke-width=\"2\" />\r\n <path d=\"M12 5A7 7 0 1 1 12 19\" fill=\"currentColor\" />\r\n </svg>\r\n </button>\r\n <button class=\"action-btn collapse-btn\" @click.stop=\"toggleCollapse\" :title=\"isCollapsed ? '展开' : '折叠'\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path\r\n :d=\"isCollapsed ? 'M18 15L12 9L6 15' : 'M6 9L12 15L18 9'\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n </button>\r\n <button class=\"action-btn minimize-btn\" @click.stop=\"toggleDialog(false)\" title=\"最小化\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M5 12H19\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n <div :class=\"['x-dialog-content', { 'is-hidden': isCollapsed }]\" :style=\"{ opacity: isCollapsed ? 0 : 1 }\">\r\n <iframe\r\n ref=\"iframeRef\"\r\n :src=\"chatbotUrl\"\r\n class=\"x-iframe\"\r\n allow=\"microphone\"\r\n frameborder=\"0\"\r\n @load=\"handleIframeLoad\"\r\n ></iframe>\r\n </div>\r\n </div>\r\n </transition>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, reactive, onBeforeUnmount, computed, nextTick, watch } from 'vue';\r\nimport VoiceStatus from './voice-status.vue';\r\nimport ExecutionStatus from './execution-status.vue';\r\nimport { injectStrict, AiChatbotXKey } from '../injection-key';\r\nimport { WakeWordDetectorStandalone, SpeechTranscriberStandalone } from 'web-voice-kit';\r\nimport { ensureMicrophonePermission, matchVoiceCommand } from '../lib/utils';\r\n\r\ninterface XSize {\r\n width: number;\r\n height: number;\r\n}\r\ninterface xDialogSize {\r\n width: number;\r\n height: number;\r\n}\r\n\r\nconst props = defineProps<{\r\n xLogo?: string;\r\n xSize?: XSize;\r\n xTitle?: string;\r\n xTheme?: 'light' | 'dark' | 'system';\r\n xDialogSize?: xDialogSize;\r\n wakeWords?: string[];\r\n modelPath?: string;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n (e: 'start-transcribing'): void;\r\n (e: 'stop-transcribing'): void;\r\n (e: 'wakeUp', isWake: boolean): void;\r\n}>();\r\n\r\nconst aiChatbotX = injectStrict(AiChatbotXKey);\r\n\r\nconst chatbotUrl = ref('');\r\nconst voiceStatus = ref<'wake' | 'listening' | 'standby'>('standby');\r\nconst wakeAnimating = ref(false);\r\nconst wakeResponses = ['在呢', '在的', '我在', '您好', '在呢,请说'];\r\nconst transcriptionText = ref<string>('');\r\nconst isTranscribing = ref(false);\r\nconst isProcessing = ref(false);\r\nconst visible = ref(false);\r\nconst isCollapsed = ref(false);\r\nconst fabRef = ref<HTMLElement | null>(null);\r\nconst positionReady = ref(false);\r\n\r\nlet detector: WakeWordDetectorStandalone | null = null;\r\nlet transcriber: SpeechTranscriberStandalone | null = null;\r\n\r\nconst isInitializing = ref(false);\r\nconst initError = ref<string>('');\r\n\r\n// 获取系统主题\r\nconst getSystemTheme = (): 'light' | 'dark' => {\r\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\r\n};\r\nconst currentTheme = ref<'light' | 'dark'>(props.xTheme === 'system' ? getSystemTheme() : props.xTheme || 'light');\r\n\r\n// 切换主题\r\nconst cycleTheme = async () => {\r\n currentTheme.value = currentTheme.value === 'light' ? 'dark' : 'light';\r\n aiChatbotX.setTheme(currentTheme.value);\r\n};\r\n\r\n// 开启新对话\r\nconst startNewConversation = () => {\r\n aiChatbotX.startNewConversation();\r\n};\r\n\r\nconst themeTooltip = computed(() => (currentTheme.value === 'light' ? '切换到深色模式' : '切换到浅色模式'));\r\n\r\nconst voiceButtonTooltip = computed(() => {\r\n if (isInitializing.value) return '初始化中...';\r\n if (initError.value) return `错误: ${initError.value}`;\r\n\r\n switch (voiceStatus.value) {\r\n case 'standby':\r\n return '开启语音监听';\r\n case 'listening':\r\n return '监听中,等待唤醒词...';\r\n case 'wake':\r\n return '已唤醒!';\r\n default:\r\n return '语音监听';\r\n }\r\n});\r\n\r\nif (props.xTheme === 'system') {\r\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n const handleThemeChange = (e: MediaQueryListEvent) => {\r\n currentTheme.value = e.matches ? 'dark' : 'light';\r\n };\r\n mediaQuery.addEventListener('change', handleThemeChange);\r\n onBeforeUnmount(() => {\r\n mediaQuery.removeEventListener('change', handleThemeChange);\r\n });\r\n}\r\n\r\n// 初始化语音监听器\r\nconst initVoiceDetector = () => {\r\n if (detector || isInitializing.value) return;\r\n\r\n isInitializing.value = true;\r\n initError.value = '';\r\n\r\n if (!props.modelPath) {\r\n initError.value = '未提供语音模型文件';\r\n isInitializing.value = false;\r\n return;\r\n }\r\n\r\n try {\r\n detector = new WakeWordDetectorStandalone({\r\n modelPath: props.modelPath,\r\n sampleRate: 16000,\r\n usePartial: true,\r\n autoReset: {\r\n enabled: true,\r\n resetDelayMs: 5000,\r\n },\r\n });\r\n\r\n const wakeWords = props.wakeWords || ['你好', '您好'];\r\n detector.setWakeWords(wakeWords);\r\n\r\n detector.onWake(() => {\r\n console.log('[VoiceDetector] 检测到唤醒词');\r\n // 触发唤醒动画\r\n wakeAnimating.value = true;\r\n // 语音播报回应\r\n playWakeResponse();\r\n // 通知 web 端\r\n emit('wakeUp', true);\r\n aiChatbotX.weak();\r\n aiChatbotX.openDialog();\r\n // 动画结束后恢复\r\n setTimeout(() => {\r\n wakeAnimating.value = false;\r\n }, 1500);\r\n // 保持 listening 状态,继续后台唤醒监听\r\n voiceStatus.value = 'listening';\r\n });\r\n\r\n detector.onError((error: any) => {\r\n console.error('[VoiceDetector] 错误:', error);\r\n initError.value = error.message;\r\n voiceStatus.value = 'standby';\r\n isTranscribing.value = false;\r\n\r\n if (error.message.includes('permission')) {\r\n transcriptionText.value = '需要麦克风权限';\r\n } else if (error.message.includes('model')) {\r\n transcriptionText.value = '模型加载失败';\r\n } else {\r\n transcriptionText.value = '初始化失败';\r\n }\r\n\r\n setTimeout(() => {\r\n transcriptionText.value = '';\r\n }, 3000);\r\n });\r\n\r\n console.log('[VoiceDetector] 初始化成功');\r\n } catch (error) {\r\n console.error('[VoiceDetector] 初始化失败:', error);\r\n initError.value = error instanceof Error ? error.message : '初始化失败';\r\n voiceStatus.value = 'standby';\r\n isTranscribing.value = false;\r\n } finally {\r\n isInitializing.value = false;\r\n }\r\n};\r\n\r\n// 语音播报唤醒回应(使用 Web Speech Synthesis API,无需音频文件)\r\nconst playWakeResponse = () => {\r\n try {\r\n if (typeof window === 'undefined' || !window.speechSynthesis) {\r\n console.warn('[TTS] SpeechSynthesis API 不可用');\r\n return;\r\n }\r\n const text = wakeResponses[Math.floor(Math.random() * wakeResponses.length)];\r\n const utterance = new SpeechSynthesisUtterance(text);\r\n utterance.lang = 'zh-CN';\r\n utterance.rate = 1.0;\r\n utterance.pitch = 0.8;\r\n utterance.volume = 0.9;\r\n // 优先选择中文男声\r\n const voices = window.speechSynthesis.getVoices();\r\n const maleVoice = voices.find((v) => v.lang.startsWith('zh') && /male|男/i.test(v.name));\r\n const zhVoice = maleVoice || voices.find((v) => v.lang.startsWith('zh'));\r\n if (zhVoice) {\r\n utterance.voice = zhVoice;\r\n }\r\n window.speechSynthesis.speak(utterance);\r\n } catch (error) {\r\n console.error('[TTS] 播报失败:', error);\r\n }\r\n};\r\n\r\n// 初始化转写器\r\nfunction initTranscriber() {\r\n if (transcriber) return;\r\n\r\n try {\r\n const { appId, apiKey, websocketUrl } = aiChatbotX.voiceConfig();\r\n if (!appId || !apiKey || !websocketUrl) {\r\n initError.value = '未配置语音配置';\r\n voiceStatus.value = 'standby';\r\n isTranscribing.value = false;\r\n return;\r\n }\r\n transcriber = new SpeechTranscriberStandalone({\r\n appId,\r\n apiKey,\r\n websocketUrl,\r\n autoStop: {\r\n enabled: true,\r\n silenceTimeoutMs: 3000,\r\n noSpeechTimeoutMs: 5000,\r\n maxDurationMs: 60000,\r\n },\r\n });\r\n\r\n transcriber.onResult((result) => {\r\n transcriptionText.value = result.transcript;\r\n });\r\n\r\n transcriber.onAutoStop(async () => {\r\n console.log('[Transcriber] Auto Stop');\r\n\r\n // 保存当前转写文本,用于后续处理\r\n const currentText = transcriptionText.value;\r\n\r\n // 停止转写\r\n await stopTranscribing();\r\n\r\n // 如果没有转写内容,直接返回后台监听\r\n if (!currentText || !currentText.trim()) {\r\n console.log('[Transcriber] No transcription text, returning to listening');\r\n transcriptionText.value = '';\r\n voiceStatus.value = 'listening';\r\n return;\r\n }\r\n\r\n // 进入处理中状态,显示优雅的等待动画\r\n isProcessing.value = true;\r\n transcriptionText.value = currentText; // 保持显示用户说的内容\r\n\r\n try {\r\n const commands = await aiChatbotX.hostCommads();\r\n const result = await aiChatbotX.recognition(currentText, commands);\r\n\r\n // 如果是命令类型,执行匹配的命令\r\n if (result?.data?.intent === 'command' && result?.data?.matchedCommands) {\r\n const matchedCommands = result.data.matchedCommands;\r\n\r\n for (const cmd of matchedCommands) {\r\n try {\r\n // 将参数对象转换为参数数组\r\n const args = cmd.parameters ? Object.values(cmd.parameters) : [];\r\n\r\n // 执行宿主命令\r\n const cmdResult = await aiChatbotX.executeCommand(cmd.name, args);\r\n console.log(`Command ${cmd.name} executed successfully:`, cmdResult);\r\n } catch (error) {\r\n console.error(`Failed to execute command ${cmd.name}:`, error);\r\n }\r\n }\r\n } else {\r\n aiChatbotX.appendMessage(currentText);\r\n toggleDialog(true);\r\n }\r\n } finally {\r\n // 处理完成,返回后台监听状态\r\n isProcessing.value = false;\r\n transcriptionText.value = '';\r\n voiceStatus.value = 'listening';\r\n }\r\n });\r\n\r\n transcriber.onError((error) => {\r\n console.error('[Transcriber] Error:', error);\r\n stopTranscribing();\r\n transcriptionText.value = '转写错误';\r\n setTimeout(() => {\r\n transcriptionText.value = '';\r\n voiceStatus.value = 'listening';\r\n }, 2000);\r\n });\r\n\r\n console.log('[Transcriber] 初始化成功');\r\n } catch (error) {\r\n console.error('[Transcriber] 初始化失败:', error);\r\n voiceStatus.value = 'standby';\r\n initError.value = error instanceof Error ? error.message : '转写初始化失败';\r\n }\r\n}\r\n\r\nconst startTranscribing = async () => {\r\n if (!transcriber) {\r\n initTranscriber();\r\n if (!transcriber) return;\r\n }\r\n\r\n try {\r\n emit('start-transcribing');\r\n await transcriber.start();\r\n isTranscribing.value = true;\r\n transcriptionText.value = '';\r\n } catch (error) {\r\n console.error('[Transcriber] 启动失败:', error);\r\n transcriptionText.value = '转写启动失败';\r\n setTimeout(() => {\r\n transcriptionText.value = '';\r\n }, 2000);\r\n }\r\n};\r\n\r\nconst stopTranscribing = async () => {\r\n if (transcriber && transcriber.isActive()) {\r\n try {\r\n await transcriber.stop();\r\n isTranscribing.value = false;\r\n emit('stop-transcribing');\r\n } catch (error) {\r\n console.error('[Transcriber] 停止失败:', error);\r\n }\r\n }\r\n};\r\n\r\nconst toggleVoiceMode = async (targetState?: boolean) => {\r\n const permission = await ensureMicrophonePermission();\r\n if (!permission) return;\r\n\r\n if (isInitializing.value) return;\r\n\r\n if (!detector) {\r\n await initVoiceDetector();\r\n if (!detector) return;\r\n }\r\n\r\n // 核心逻辑:如果传了参数,目标就是参数值;如果不传,目标就是当前状态的反值\r\n // 假设 \"listening\" 为开启状态,其余(如 \"standby\")为关闭状态\r\n const isCurrentlyListening = voiceStatus.value === 'listening';\r\n const shouldStart = targetState !== undefined ? targetState : !isCurrentlyListening;\r\n\r\n // 如果当前状态已经符合目标状态,直接返回(防止重复开启或关闭)\r\n if (shouldStart === isCurrentlyListening) return;\r\n\r\n try {\r\n if (shouldStart) {\r\n // 执行开启逻辑\r\n console.log('[VoiceDetector] 强制/自动启动监听...');\r\n await detector.start();\r\n voiceStatus.value = 'listening';\r\n transcriptionText.value = '';\r\n isTranscribing.value = false;\r\n } else {\r\n // 执行关闭逻辑\r\n console.log('[VoiceDetector] 强制/自动停止监听...');\r\n await detector.stop();\r\n stopTranscribing();\r\n voiceStatus.value = 'standby';\r\n transcriptionText.value = '';\r\n isTranscribing.value = false;\r\n }\r\n } catch (error) {\r\n console.error('[VoiceDetector] 操作失败:', error);\r\n voiceStatus.value = 'standby';\r\n transcriptionText.value = '操作失败';\r\n isTranscribing.value = false;\r\n\r\n setTimeout(() => {\r\n transcriptionText.value = '';\r\n }, 2000);\r\n }\r\n};\r\n\r\nconst position = reactive({ x: 0, y: 0 });\r\nconst containerWidth = ref(props.xDialogSize?.width || 420);\r\nconst containerHeight = ref(props.xDialogSize?.height || 600);\r\nconst isFirstOpen = ref(true);\r\nconst FAB_SAFE_GAP = 24;\r\n\r\nconst validatePosition = (x: number, y: number, width: number, height: number) => {\r\n const margin = 20;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n\r\n const maxX = viewportWidth - width - margin;\r\n const maxY = viewportHeight - height - margin;\r\n\r\n return {\r\n x: Math.max(margin, Math.min(x, maxX)),\r\n y: Math.max(margin, Math.min(y, maxY)),\r\n };\r\n};\r\n\r\nconst getOverlapArea = (\r\n rectA: { left: number; right: number; top: number; bottom: number },\r\n rectB: { left: number; right: number; top: number; bottom: number },\r\n) => {\r\n const overlapX = Math.max(0, Math.min(rectA.right, rectB.right) - Math.max(rectA.left, rectB.left));\r\n const overlapY = Math.max(0, Math.min(rectA.bottom, rectB.bottom) - Math.max(rectA.top, rectB.top));\r\n return overlapX * overlapY;\r\n};\r\n\r\nconst keepDistanceFromFab = (x: number, y: number, width: number, height: number) => {\r\n if (!fabRef.value) return { x, y };\r\n\r\n const fabRect = fabRef.value.getBoundingClientRect();\r\n const safeFabRect = {\r\n left: fabRect.left - FAB_SAFE_GAP,\r\n right: fabRect.right + FAB_SAFE_GAP,\r\n top: fabRect.top - FAB_SAFE_GAP,\r\n bottom: fabRect.bottom + FAB_SAFE_GAP,\r\n };\r\n\r\n const candidates = [\r\n { x, y },\r\n { x: safeFabRect.left - width - FAB_SAFE_GAP, y },\r\n { x: safeFabRect.right + FAB_SAFE_GAP, y },\r\n { x, y: safeFabRect.top - height - FAB_SAFE_GAP },\r\n { x, y: safeFabRect.bottom + FAB_SAFE_GAP },\r\n ];\r\n\r\n let best = validatePosition(candidates[0].x, candidates[0].y, width, height);\r\n let bestOverlap = getOverlapArea(\r\n { left: best.x, right: best.x + width, top: best.y, bottom: best.y + height },\r\n safeFabRect,\r\n );\r\n\r\n for (let i = 1; i < candidates.length; i++) {\r\n const validated = validatePosition(candidates[i].x, candidates[i].y, width, height);\r\n const overlap = getOverlapArea(\r\n { left: validated.x, right: validated.x + width, top: validated.y, bottom: validated.y + height },\r\n safeFabRect,\r\n );\r\n\r\n if (overlap < bestOverlap) {\r\n best = validated;\r\n bestOverlap = overlap;\r\n if (overlap === 0) break;\r\n }\r\n }\r\n\r\n return best;\r\n};\r\n\r\nconst calculateInitialPosition = () => {\r\n if (!fabRef.value) return;\r\n\r\n const fabRect = fabRef.value.getBoundingClientRect();\r\n const dialogWidth = containerWidth.value;\r\n const dialogHeight = containerHeight.value;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n const margin = 20; // 增加间距,确保不遮挡\r\n const minMargin = 20; // 最小边距\r\n\r\n let x = 0;\r\n let y = 0;\r\n\r\n // 优先尝试在 FAB 左侧定位\r\n const leftX = fabRect.left - dialogWidth - margin;\r\n\r\n if (leftX >= minMargin) {\r\n // 左侧有足够空间\r\n x = leftX;\r\n // Y 轴对齐 FAB 顶部,但确保不超出视口\r\n y = fabRect.top;\r\n\r\n // 如果对话框底部超出视口,向上调整\r\n if (y + dialogHeight > viewportHeight - minMargin) {\r\n y = viewportHeight - dialogHeight - minMargin;\r\n }\r\n // 确保顶部不超出\r\n if (y < minMargin) {\r\n y = minMargin;\r\n }\r\n } else {\r\n // 左侧空间不足,尝试右侧\r\n const rightX = fabRect.right + margin;\r\n\r\n if (rightX + dialogWidth <= viewportWidth - minMargin) {\r\n // 右侧有足够空间\r\n x = rightX;\r\n y = fabRect.top;\r\n\r\n // Y 轴调整\r\n if (y + dialogHeight > viewportHeight - minMargin) {\r\n y = viewportHeight - dialogHeight - minMargin;\r\n }\r\n if (y < minMargin) {\r\n y = minMargin;\r\n }\r\n } else {\r\n // 左右都不够,居中显示,并确保不遮挡 FAB\r\n x = (viewportWidth - dialogWidth) / 2;\r\n\r\n // 尝试在 FAB 上方\r\n const aboveY = fabRect.top - dialogHeight - margin;\r\n if (aboveY >= minMargin) {\r\n y = aboveY;\r\n } else {\r\n // 在 FAB 下方\r\n const belowY = fabRect.bottom + margin;\r\n if (belowY + dialogHeight <= viewportHeight - minMargin) {\r\n y = belowY;\r\n } else {\r\n // 垂直居中\r\n y = (viewportHeight - dialogHeight) / 2;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 最终验证位置\r\n const validated = validatePosition(x, y, dialogWidth, dialogHeight);\r\n const separated = keepDistanceFromFab(validated.x, validated.y, dialogWidth, dialogHeight);\r\n position.x = separated.x;\r\n position.y = separated.y;\r\n};\r\n\r\nconst toggleCollapse = async () => {\r\n isCollapsed.value = !isCollapsed.value;\r\n\r\n nextTick(() => {\r\n const currentHeight = isCollapsed.value ? 60 : containerHeight.value;\r\n const validated = validatePosition(position.x, position.y, containerWidth.value, currentHeight);\r\n const separated = keepDistanceFromFab(validated.x, validated.y, containerWidth.value, currentHeight);\r\n position.x = separated.x;\r\n position.y = separated.y;\r\n });\r\n};\r\n\r\nconst drag = reactive({\r\n isDragging: false,\r\n startX: 0,\r\n startY: 0,\r\n offsetX: 0,\r\n offsetY: 0,\r\n});\r\n\r\nconst startDrag = (e: MouseEvent) => {\r\n drag.isDragging = true;\r\n drag.startX = e.clientX;\r\n drag.startY = e.clientY;\r\n drag.offsetX = position.x;\r\n drag.offsetY = position.y;\r\n document.addEventListener('mousemove', onDrag);\r\n document.addEventListener('mouseup', stopDrag);\r\n};\r\n\r\nconst onDrag = (e: MouseEvent) => {\r\n if (!drag.isDragging) return;\r\n\r\n const newX = drag.offsetX + (e.clientX - drag.startX);\r\n const newY = drag.offsetY + (e.clientY - drag.startY);\r\n\r\n const validated = validatePosition(newX, newY, containerWidth.value, isCollapsed.value ? 60 : containerHeight.value);\r\n\r\n position.x = validated.x;\r\n position.y = validated.y;\r\n};\r\n\r\nconst stopDrag = () => {\r\n drag.isDragging = false;\r\n document.removeEventListener('mousemove', onDrag);\r\n document.removeEventListener('mouseup', stopDrag);\r\n};\r\n\r\nconst toggleDialog = async (state: boolean) => {\r\n if (state) {\r\n visible.value = true;\r\n positionReady.value = false;\r\n emit('wakeUp', true);\r\n await nextTick();\r\n\r\n if (isFirstOpen.value) {\r\n calculateInitialPosition();\r\n isFirstOpen.value = false;\r\n } else {\r\n const validated = validatePosition(\r\n position.x,\r\n position.y,\r\n containerWidth.value,\r\n isCollapsed.value ? 60 : containerHeight.value,\r\n );\r\n const separated = keepDistanceFromFab(\r\n validated.x,\r\n validated.y,\r\n containerWidth.value,\r\n isCollapsed.value ? 60 : containerHeight.value,\r\n );\r\n position.x = separated.x;\r\n position.y = separated.y;\r\n }\r\n\r\n await nextTick();\r\n positionReady.value = true;\r\n } else {\r\n positionReady.value = false;\r\n visible.value = false;\r\n isCollapsed.value = false;\r\n emit('wakeUp', false);\r\n }\r\n};\r\n\r\nconst handleIframeLoad = (event: Event) => {\r\n aiChatbotX.setIframeElement(event.target as HTMLIFrameElement);\r\n aiChatbotX.setTheme('dark');\r\n};\r\n\r\nwatch(\r\n () => [aiChatbotX.chatbotUrl()],\r\n ([url]) => {\r\n console.log('[AiChatbotX] 初始化', url);\r\n if (url) {\r\n chatbotUrl.value = `${url}/app/${aiChatbotX.appId()}?token=${aiChatbotX.appToken()}`;\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n\r\nonBeforeUnmount(async () => {\r\n if (detector) {\r\n try {\r\n if (detector.isActive()) {\r\n await detector.stop();\r\n }\r\n detector = null;\r\n } catch (error) {\r\n console.error('[VoiceDetector] 清理失败:', error);\r\n }\r\n }\r\n if (transcriber) {\r\n try {\r\n if (transcriber.isActive()) {\r\n await transcriber.stop();\r\n }\r\n transcriber = null;\r\n } catch (error) {\r\n console.error('[Transcriber] 清理失败:', error);\r\n }\r\n }\r\n});\r\n\r\naiChatbotX?.registerVoiceMethods({\r\n start: () => toggleVoiceMode(true),\r\n stop: () => toggleVoiceMode(false),\r\n openDialog: () => toggleDialog(true),\r\n closeDialog: () => toggleDialog(false),\r\n toggleCollapse: () => toggleCollapse(),\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.sime-x {\r\n --bg-primary: #ffffff;\r\n --bg-secondary: #f8fafc;\r\n --bg-header: #0f172a;\r\n --text-primary: #0f172a;\r\n --text-secondary: #64748b;\r\n --text-header: #ffffff;\r\n --border-color: #e2e8f0;\r\n --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);\r\n --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);\r\n --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.15);\r\n --accent-color: #3b82f6;\r\n --hover-bg: rgba(0, 0, 0, 0.05);\r\n\r\n &[data-theme='dark'] {\r\n --bg-primary: #1e293b;\r\n --bg-secondary: #0f172a;\r\n --bg-header: #0f172a;\r\n --text-primary: #f1f5f9;\r\n --text-secondary: #94a3b8;\r\n --text-header: #f1f5f9;\r\n --border-color: #334155;\r\n --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);\r\n --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);\r\n --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.5);\r\n --accent-color: #60a5fa;\r\n --hover-bg: rgba(255, 255, 255, 0.1);\r\n }\r\n}\r\n\r\n.is-hidden {\r\n height: 0;\r\n opacity: 0 !important;\r\n pointer-events: none !important;\r\n visibility: hidden !important;\r\n}\r\n\r\n.assistant-fab {\r\n position: relative;\r\n cursor: pointer;\r\n overflow: visible;\r\n filter: drop-shadow(var(--shadow-md));\r\n opacity: 1;\r\n pointer-events: auto;\r\n transition: all 0.3s ease;\r\n\r\n &:hover {\r\n transform: scale(1.05);\r\n }\r\n\r\n &:active {\r\n transform: scale(0.95);\r\n }\r\n\r\n .voice-status {\r\n position: absolute;\r\n right: 100%;\r\n margin-right: 20px;\r\n }\r\n\r\n img {\r\n display: block;\r\n position: relative;\r\n object-fit: contain;\r\n }\r\n\r\n .fab-pulse {\r\n position: absolute;\r\n inset: -4px;\r\n border-radius: 50%;\r\n border: 2px solid var(--accent-color);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n@keyframes pulse {\r\n 0%,\r\n 100% {\r\n opacity: 0;\r\n transform: scale(1);\r\n }\r\n 50% {\r\n opacity: 0.5;\r\n transform: scale(1.1);\r\n }\r\n}\r\n\r\n.x-dialog-container {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n transform: translate(var(--dialog-x), var(--dialog-y));\r\n border-radius: 16px;\r\n display: flex;\r\n flex-direction: column;\r\n cursor: grab;\r\n overflow: hidden;\r\n z-index: 1100;\r\n opacity: 0;\r\n pointer-events: none;\r\n\r\n &.position-ready {\r\n opacity: 1;\r\n pointer-events: auto;\r\n }\r\n\r\n &:active {\r\n cursor: grabbing;\r\n }\r\n\r\n &.collapsed {\r\n .x-dialog-header {\r\n border-radius: 16px;\r\n }\r\n }\r\n\r\n .x-dialog-header {\r\n position: relative;\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n background: var(--bg-header);\r\n padding: 12px 16px;\r\n cursor: move;\r\n user-select: none;\r\n border-bottom: 1px solid var(--border-color);\r\n\r\n .header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n\r\n .logo-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 36px;\r\n height: 36px;\r\n transition: transform 0.2s ease;\r\n\r\n .logo {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: contain;\r\n }\r\n }\r\n\r\n .title {\r\n font-weight: 600;\r\n font-size: 15px;\r\n color: var(--text-header);\r\n letter-spacing: -0.01em;\r\n }\r\n }\r\n\r\n .actions {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n\r\n .action-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border: none;\r\n background: transparent;\r\n cursor: pointer;\r\n color: var(--text-secondary);\r\n transition: all 0.2s ease;\r\n border-radius: 8px;\r\n\r\n &:hover {\r\n color: var(--text-header);\r\n background: var(--hover-bg);\r\n transform: translateY(-1px);\r\n }\r\n\r\n &:active {\r\n transform: translateY(0);\r\n }\r\n\r\n svg {\r\n transition: transform 0.2s ease;\r\n }\r\n\r\n &.collapse-btn:hover svg {\r\n transform: scale(1.1);\r\n }\r\n }\r\n }\r\n }\r\n\r\n .voice-container {\r\n width: 100%;\r\n opacity: 1;\r\n pointer-events: auto;\r\n }\r\n\r\n .x-dialog-content {\r\n flex: 1;\r\n display: flex;\r\n overflow: hidden;\r\n background: var(--bg-secondary);\r\n opacity: 1;\r\n pointer-events: auto;\r\n }\r\n\r\n .x-iframe {\r\n flex: 1;\r\n width: 100%;\r\n height: 100%;\r\n border: none;\r\n }\r\n\r\n .resize-handle {\r\n position: absolute;\r\n bottom: 0;\r\n right: 0;\r\n width: 40px;\r\n height: 40px;\r\n cursor: nwse-resize;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--text-secondary);\r\n transition: all 0.2s ease;\r\n background: linear-gradient(135deg, transparent 50%, var(--bg-primary) 50%);\r\n border-bottom-right-radius: 16px;\r\n z-index: 10;\r\n\r\n &:hover {\r\n color: var(--accent-color);\r\n background: linear-gradient(135deg, transparent 50%, var(--hover-bg) 50%);\r\n }\r\n\r\n svg {\r\n width: 16px;\r\n height: 16px;\r\n transform: rotate(0deg);\r\n transition: transform 0.2s ease;\r\n }\r\n\r\n &:hover svg {\r\n transform: scale(1.2);\r\n }\r\n }\r\n}\r\n/* 语音按钮状态样式 */\r\n.action-btn.theme-btn {\r\n position: relative;\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.action-btn.theme-btn.active {\r\n background: rgba(59, 130, 246, 0.1);\r\n color: #3b82f6;\r\n}\r\n\r\n.action-btn.theme-btn.listening {\r\n animation: pulse-listening 2s ease-in-out infinite;\r\n}\r\n\r\n.action-btn.theme-btn.woke {\r\n background: rgba(34, 197, 94, 0.2);\r\n color: #22c55e;\r\n animation: pulse-wake 0.6s ease-in-out;\r\n}\r\n\r\n@keyframes pulse-listening {\r\n 0%,\r\n 100% {\r\n box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);\r\n }\r\n 50% {\r\n box-shadow: 0 0 0 8px rgba(59, 130, 246, 0);\r\n }\r\n}\r\n\r\n@keyframes pulse-wake {\r\n 0%,\r\n 100% {\r\n transform: scale(1);\r\n }\r\n 50% {\r\n transform: scale(1.1);\r\n }\r\n}\r\n\r\n/* 语音指示器 */\r\n.voice-indicator {\r\n position: absolute;\r\n top: 4px;\r\n right: 4px;\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: #3b82f6;\r\n}\r\n\r\n.action-btn.theme-btn.listening .voice-indicator {\r\n animation: blink 1.5s ease-in-out infinite;\r\n}\r\n\r\n.action-btn.theme-btn.woke .voice-indicator {\r\n background: #22c55e;\r\n animation: none;\r\n}\r\n\r\n@keyframes blink {\r\n 0%,\r\n 100% {\r\n opacity: 1;\r\n }\r\n 50% {\r\n opacity: 0.3;\r\n }\r\n}\r\n\r\n/* 麦克风唤醒动画 */\r\n.listening-badge.wake-active {\r\n background-color: rgba(34, 197, 94, 0.85);\r\n animation: wake-badge-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;\r\n\r\n .listening-icon {\r\n animation: wake-icon-flash 1.5s ease-in-out;\r\n }\r\n\r\n &::after {\r\n content: '';\r\n position: absolute;\r\n inset: -3px;\r\n border-radius: 50%;\r\n border: 2px solid #22c55e;\r\n animation: wake-mic-ring 1.2s ease-out forwards;\r\n pointer-events: none;\r\n }\r\n}\r\n\r\n@keyframes wake-badge-pop {\r\n 0% {\r\n transform: scale(1);\r\n }\r\n 50% {\r\n transform: scale(1.4);\r\n }\r\n 100% {\r\n transform: scale(1);\r\n }\r\n}\r\n\r\n@keyframes wake-icon-flash {\r\n 0%,\r\n 100% {\r\n color: #ffffff;\r\n }\r\n 25% {\r\n color: #bbf7d0;\r\n }\r\n 50% {\r\n color: #ffffff;\r\n }\r\n 75% {\r\n color: #bbf7d0;\r\n }\r\n}\r\n\r\n@keyframes wake-mic-ring {\r\n 0% {\r\n transform: scale(1);\r\n opacity: 0.8;\r\n }\r\n 100% {\r\n transform: scale(2.2);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n/* FAB 头像容器 */\r\n.fab-avatar-wrapper {\r\n position: relative;\r\n display: flex; /* 改用 flex 可以消除图片底部的行高间隙 */\r\n align-items: center;\r\n justify-content: center;\r\n width: fit-content; /* 宽度随内容自适应 */\r\n margin: 0 auto;\r\n}\r\n\r\n/* 2. 修正图片样式 */\r\n.fab-avatar-wrapper img {\r\n display: block;\r\n max-width: 100%;\r\n height: auto;\r\n /* 移除原本 style 里的 width/height 可能造成的拉伸,保持比例 */\r\n}\r\n\r\n/* 3. 优化徽章定位 */\r\n.listening-badge {\r\n position: absolute;\r\n /* 强制定位到容器边缘 */\r\n top: 0;\r\n right: 0;\r\n /* 使用 transform 偏移,让徽章中心点落在图片的右上角顶点上 */\r\n transform: translate(30%, -30%);\r\n\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background-color: rgba(0, 0, 0, 0.75); /* 稍微加深一点 */\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 10;\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\r\n\r\n /* 继承你原有的动画 */\r\n animation: badge-float 5s ease-in-out infinite;\r\n pointer-events: none; /* 防止遮挡头像点击 */\r\n}\r\n\r\n/* 4. 调整声波扩散的基准 */\r\n.wave {\r\n position: absolute;\r\n /* 确保声波从徽章中心扩散,而不是从容器左上角 */\r\n inset: -2px;\r\n border-radius: 50%;\r\n border: 2px solid rgba(59, 130, 246, 0.6); /* 声波颜色建议跟主题色一致 */\r\n animation: wave-expand 3s ease-out infinite;\r\n}\r\n\r\n/* 优化后的监听状态徽章 */\r\n.listening-badge {\r\n position: absolute;\r\n top: -8px;\r\n right: -8px;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n background-color: rgba(0, 0, 0, 0.651);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 10;\r\n animation: badge-float 5s ease-in-out infinite;\r\n}\r\n\r\n/* 徽章浮动动画 */\r\n@keyframes badge-float {\r\n 0%,\r\n 100% {\r\n transform: translateY(0) scale(1);\r\n }\r\n 50% {\r\n transform: translateY(-2px) scale(1.05);\r\n }\r\n}\r\n\r\n/* 声波容器 */\r\n.listening-waves {\r\n position: absolute;\r\n inset: 0;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n/* 声波动画 */\r\n.wave {\r\n position: absolute;\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 50%;\r\n border: 2px solid rgba(255, 255, 255, 0.6);\r\n animation: wave-expand 3s ease-out infinite;\r\n}\r\n\r\n.wave-1 {\r\n animation-delay: 0s;\r\n}\r\n\r\n.wave-2 {\r\n animation-delay: 0.6s;\r\n}\r\n\r\n.wave-3 {\r\n animation-delay: 1.2s;\r\n}\r\n\r\n@keyframes wave-expand {\r\n 0% {\r\n transform: scale(0.8);\r\n opacity: 0.8;\r\n }\r\n 100% {\r\n transform: scale(1.2);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n/* 麦克风图标 */\r\n.listening-icon {\r\n position: relative;\r\n width: 24px;\r\n height: 24px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #ffffff;\r\n z-index: 1;\r\n animation: icon-pulse 2.5s ease-in-out infinite;\r\n}\r\n\r\n@keyframes icon-pulse {\r\n 0%,\r\n 100% {\r\n transform: scale(1);\r\n }\r\n 50% {\r\n transform: scale(1.1);\r\n }\r\n}\r\n\r\n/* 徽章淡入淡出动画 */\r\n.indicator-fade-enter-active {\r\n transition: all 1s cubic-bezier(0.34, 1.56, 0.64, 1);\r\n}\r\n\r\n.indicator-fade-leave-active {\r\n transition: all 1s ease;\r\n}\r\n\r\n.indicator-fade-enter-from {\r\n opacity: 0;\r\n transform: scale(0.3) rotate(-180deg);\r\n}\r\n\r\n.indicator-fade-leave-to {\r\n opacity: 0;\r\n transform: scale(0.5) rotate(180deg);\r\n}\r\n\r\n/* FAB 脉冲效果 */\r\n.fab-pulse {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n border-radius: 50%;\r\n pointer-events: none;\r\n}\r\n\r\n.fab-pulse.active {\r\n animation: fab-pulse 2s ease-in-out infinite;\r\n}\r\n\r\n@keyframes fab-pulse {\r\n 0%,\r\n 100% {\r\n box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);\r\n }\r\n 50% {\r\n box-shadow: 0 0 0 20px rgba(59, 130, 246, 0);\r\n }\r\n}\r\n\r\n/* 深色模式适配 */\r\n[data-theme='dark'] .action-btn.theme-btn.active {\r\n background: rgba(59, 130, 246, 0.2);\r\n}\r\n\r\n[data-theme='dark'] .action-btn.theme-btn.woke {\r\n background: rgba(34, 197, 94, 0.3);\r\n}\r\n</style>\r\n"],"names":["clientCommandKey","_renderSlot","_createBlock","_Transition","_createElementBlock","_normalizeClass","_createElementVNode","_hoisted_1","_hoisted_2","_hoisted_3","_toDisplayString","_createVNode","_hoisted_4","_hoisted_5","_openBlock","_normalizeStyle"],"mappings":"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"}