@siact/sime-x-vue 0.0.2 → 0.0.4

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.
@@ -1,6 +1,6 @@
1
1
  import { inject, defineComponent, shallowRef, provide, renderSlot, computed, openBlock, createBlock, Transition, withCtx, createElementBlock, normalizeClass, createElementVNode, toDisplayString, createVNode, createCommentVNode, ref, onBeforeUnmount, reactive, watch, normalizeStyle, withModifiers, nextTick } from 'vue';
2
2
  import { HostBridge } from '@siact/sime-bridge';
3
- import { WakeWordDetectorStandalone, SpeechTranscriberStandalone } from 'web-voice-kit';
3
+ import { WakeWordDetectorStandalone } from 'web-voice-kit';
4
4
 
5
5
  const AiChatbotXKey = Symbol("sime-x");
6
6
  function injectStrict(key, defaultValue, treatDefaultAsFactory) {
@@ -317,30 +317,26 @@ const ensureMicrophonePermission = async () => {
317
317
  const _hoisted_1 = ["data-theme"];
318
318
  const _hoisted_2 = { class: "fab-avatar-wrapper" };
319
319
  const _hoisted_3 = ["src"];
320
- const _hoisted_4 = {
321
- key: 0,
322
- class: "listening-badge"
323
- };
324
- const _hoisted_5 = { class: "header-left" };
325
- const _hoisted_6 = { class: "logo-icon" };
326
- const _hoisted_7 = ["src"];
327
- const _hoisted_8 = { class: "title" };
328
- const _hoisted_9 = { class: "actions" };
329
- const _hoisted_10 = ["title"];
330
- const _hoisted_11 = {
320
+ const _hoisted_4 = { class: "header-left" };
321
+ const _hoisted_5 = { class: "logo-icon" };
322
+ const _hoisted_6 = ["src"];
323
+ const _hoisted_7 = { class: "title" };
324
+ const _hoisted_8 = { class: "actions" };
325
+ const _hoisted_9 = ["title"];
326
+ const _hoisted_10 = {
331
327
  key: 0,
332
328
  class: "voice-indicator"
333
329
  };
330
+ const _hoisted_11 = ["title"];
334
331
  const _hoisted_12 = ["title"];
335
- const _hoisted_13 = ["title"];
336
- const _hoisted_14 = {
332
+ const _hoisted_13 = {
337
333
  width: "16",
338
334
  height: "16",
339
335
  viewBox: "0 0 24 24",
340
336
  fill: "none"
341
337
  };
342
- const _hoisted_15 = ["d"];
343
- const _hoisted_16 = ["src"];
338
+ const _hoisted_14 = ["d"];
339
+ const _hoisted_15 = ["src"];
344
340
  const _sfc_main = /* @__PURE__ */ defineComponent({
345
341
  __name: "sime-x",
346
342
  props: {
@@ -352,13 +348,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
352
348
  wakeWords: {},
353
349
  modelPath: {}
354
350
  },
355
- emits: ["start-transcribing", "stop-transcribing"],
351
+ emits: ["start-transcribing", "stop-transcribing", "wakeUp"],
356
352
  setup(__props, { emit: __emit }) {
357
353
  const props = __props;
358
354
  const emit = __emit;
359
355
  const aiChatbotX = injectStrict(AiChatbotXKey);
360
356
  const chatbotUrl = ref("");
361
357
  const voiceStatus = ref("standby");
358
+ const wakeAnimating = ref(false);
359
+ const wakeResponses = ["在呢", "在的", "我在", "您好", "在呢,请说"];
362
360
  const transcriptionText = ref("");
363
361
  const isTranscribing = ref(false);
364
362
  const isProcessing = ref(false);
@@ -367,7 +365,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
367
365
  const fabRef = ref(null);
368
366
  const positionReady = ref(false);
369
367
  let detector = null;
370
- let transcriber = null;
371
368
  const isInitializing = ref(false);
372
369
  const initError = ref("");
373
370
  const getSystemTheme = () => {
@@ -429,10 +426,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
429
426
  detector.setWakeWords(wakeWords);
430
427
  detector.onWake(() => {
431
428
  console.log("[VoiceDetector] 检测到唤醒词");
432
- voiceStatus.value = "wake";
433
- transcriptionText.value = "";
434
- isTranscribing.value = false;
435
- startTranscribing();
429
+ wakeAnimating.value = true;
430
+ playWakeResponse();
431
+ emit("wakeUp", true);
432
+ aiChatbotX.weak();
433
+ aiChatbotX.openDialog();
434
+ setTimeout(() => {
435
+ wakeAnimating.value = false;
436
+ }, 1500);
437
+ voiceStatus.value = "listening";
436
438
  });
437
439
  detector.onError((error) => {
438
440
  console.error("[VoiceDetector] 错误:", error);
@@ -460,110 +462,30 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
460
462
  isInitializing.value = false;
461
463
  }
462
464
  };
463
- function initTranscriber() {
464
- if (transcriber) return;
465
+ const playWakeResponse = () => {
465
466
  try {
466
- const { appId, apiKey, websocketUrl } = aiChatbotX.voiceConfig();
467
- if (!appId || !apiKey || !websocketUrl) {
468
- initError.value = "未配置语音配置";
469
- voiceStatus.value = "standby";
470
- isTranscribing.value = false;
467
+ if (typeof window === "undefined" || !window.speechSynthesis) {
468
+ console.warn("[TTS] SpeechSynthesis API 不可用");
471
469
  return;
472
470
  }
473
- transcriber = new SpeechTranscriberStandalone({
474
- appId,
475
- apiKey,
476
- websocketUrl,
477
- autoStop: {
478
- enabled: true,
479
- silenceTimeoutMs: 3e3,
480
- noSpeechTimeoutMs: 5e3,
481
- maxDurationMs: 6e4
482
- }
483
- });
484
- transcriber.onResult((result) => {
485
- transcriptionText.value = result.transcript;
486
- });
487
- transcriber.onAutoStop(async () => {
488
- console.log("[Transcriber] Auto Stop");
489
- const currentText = transcriptionText.value;
490
- await stopTranscribing();
491
- if (!currentText || !currentText.trim()) {
492
- console.log("[Transcriber] No transcription text, returning to listening");
493
- transcriptionText.value = "";
494
- voiceStatus.value = "listening";
495
- return;
496
- }
497
- isProcessing.value = true;
498
- transcriptionText.value = currentText;
499
- try {
500
- const commands = await aiChatbotX.hostCommads();
501
- const result = await aiChatbotX.recognition(currentText, commands);
502
- if (result?.data?.intent === "command" && result?.data?.matchedCommands) {
503
- const matchedCommands = result.data.matchedCommands;
504
- for (const cmd of matchedCommands) {
505
- try {
506
- const args = cmd.parameters ? Object.values(cmd.parameters) : [];
507
- const cmdResult = await aiChatbotX.executeCommand(cmd.name, args);
508
- console.log(`Command ${cmd.name} executed successfully:`, cmdResult);
509
- } catch (error) {
510
- console.error(`Failed to execute command ${cmd.name}:`, error);
511
- }
512
- }
513
- } else {
514
- aiChatbotX.appendMessage(currentText);
515
- toggleDialog(true);
516
- }
517
- } finally {
518
- isProcessing.value = false;
519
- transcriptionText.value = "";
520
- voiceStatus.value = "listening";
521
- }
522
- });
523
- transcriber.onError((error) => {
524
- console.error("[Transcriber] Error:", error);
525
- stopTranscribing();
526
- transcriptionText.value = "转写错误";
527
- setTimeout(() => {
528
- transcriptionText.value = "";
529
- voiceStatus.value = "listening";
530
- }, 2e3);
531
- });
532
- console.log("[Transcriber] 初始化成功");
533
- } catch (error) {
534
- console.error("[Transcriber] 初始化失败:", error);
535
- voiceStatus.value = "standby";
536
- initError.value = error instanceof Error ? error.message : "转写初始化失败";
537
- }
538
- }
539
- const startTranscribing = async () => {
540
- if (!transcriber) {
541
- initTranscriber();
542
- if (!transcriber) return;
543
- }
544
- try {
545
- emit("start-transcribing");
546
- await transcriber.start();
547
- isTranscribing.value = true;
548
- transcriptionText.value = "";
471
+ const text = wakeResponses[Math.floor(Math.random() * wakeResponses.length)];
472
+ const utterance = new SpeechSynthesisUtterance(text);
473
+ utterance.lang = "zh-CN";
474
+ utterance.rate = 1;
475
+ utterance.pitch = 0.8;
476
+ utterance.volume = 0.9;
477
+ const voices = window.speechSynthesis.getVoices();
478
+ const maleVoice = voices.find((v) => v.lang.startsWith("zh") && /male|男/i.test(v.name));
479
+ const zhVoice = maleVoice || voices.find((v) => v.lang.startsWith("zh"));
480
+ if (zhVoice) {
481
+ utterance.voice = zhVoice;
482
+ }
483
+ window.speechSynthesis.speak(utterance);
549
484
  } catch (error) {
550
- console.error("[Transcriber] 启动失败:", error);
551
- transcriptionText.value = "转写启动失败";
552
- setTimeout(() => {
553
- transcriptionText.value = "";
554
- }, 2e3);
485
+ console.error("[TTS] 播报失败:", error);
555
486
  }
556
487
  };
557
488
  const stopTranscribing = async () => {
558
- if (transcriber && transcriber.isActive()) {
559
- try {
560
- await transcriber.stop();
561
- isTranscribing.value = false;
562
- emit("stop-transcribing");
563
- } catch (error) {
564
- console.error("[Transcriber] 停止失败:", error);
565
- }
566
- }
567
489
  };
568
490
  const toggleVoiceMode = async (targetState) => {
569
491
  const permission = await ensureMicrophonePermission();
@@ -729,6 +651,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
729
651
  positionReady.value = false;
730
652
  visible.value = false;
731
653
  isCollapsed.value = false;
654
+ emit("wakeUp", false);
732
655
  }
733
656
  };
734
657
  const handleIframeLoad = (event) => {
@@ -756,16 +679,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
756
679
  console.error("[VoiceDetector] 清理失败:", error);
757
680
  }
758
681
  }
759
- if (transcriber) {
760
- try {
761
- if (transcriber.isActive()) {
762
- await transcriber.stop();
763
- }
764
- transcriber = null;
765
- } catch (error) {
766
- console.error("[Transcriber] 清理失败:", error);
767
- }
768
- }
769
682
  });
770
683
  aiChatbotX?.registerVoiceMethods({
771
684
  start: () => toggleVoiceMode(true),
@@ -810,7 +723,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
810
723
  }, null, 12, _hoisted_3),
811
724
  createVNode(Transition, { name: "indicator-fade" }, {
812
725
  default: withCtx(() => [
813
- voiceStatus.value === "listening" ? (openBlock(), createElementBlock("div", _hoisted_4, [..._cache[3] || (_cache[3] = [
726
+ voiceStatus.value === "listening" ? (openBlock(), createElementBlock("div", {
727
+ key: 0,
728
+ class: normalizeClass(["listening-badge", { "wake-active": wakeAnimating.value }])
729
+ }, [..._cache[3] || (_cache[3] = [
814
730
  createElementVNode("div", { class: "listening-waves" }, [
815
731
  createElementVNode("div", { class: "wave wave-1" }),
816
732
  createElementVNode("div", { class: "wave wave-2" }),
@@ -835,7 +751,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
835
751
  })
836
752
  ])
837
753
  ], -1)
838
- ])])) : createCommentVNode("", true)
754
+ ])], 2)) : createCommentVNode("", true)
839
755
  ]),
840
756
  _: 1
841
757
  })
@@ -869,17 +785,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
869
785
  class: "x-dialog-header",
870
786
  onMousedown: withModifiers(startDrag, ["stop"])
871
787
  }, [
872
- createElementVNode("div", _hoisted_5, [
873
- createElementVNode("div", _hoisted_6, [
788
+ createElementVNode("div", _hoisted_4, [
789
+ createElementVNode("div", _hoisted_5, [
874
790
  createElementVNode("img", {
875
791
  src: __props.xLogo ? __props.xLogo : "/sime.png",
876
792
  alt: "assistant",
877
793
  class: "logo"
878
- }, null, 8, _hoisted_7)
794
+ }, null, 8, _hoisted_6)
879
795
  ]),
880
- createElementVNode("span", _hoisted_8, toDisplayString(__props.xTitle), 1)
796
+ createElementVNode("span", _hoisted_7, toDisplayString(__props.xTitle), 1)
881
797
  ]),
882
- createElementVNode("div", _hoisted_9, [
798
+ createElementVNode("div", _hoisted_8, [
883
799
  createElementVNode("button", {
884
800
  class: "action-btn theme-btn",
885
801
  title: "开启新对话",
@@ -943,8 +859,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
943
859
  "stroke-linejoin": "round"
944
860
  })
945
861
  ], -1)),
946
- voiceStatus.value !== "standby" ? (openBlock(), createElementBlock("span", _hoisted_11)) : createCommentVNode("", true)
947
- ], 10, _hoisted_10),
862
+ voiceStatus.value !== "standby" ? (openBlock(), createElementBlock("span", _hoisted_10)) : createCommentVNode("", true)
863
+ ], 10, _hoisted_9),
948
864
  createElementVNode("button", {
949
865
  class: "action-btn theme-btn",
950
866
  onClick: withModifiers(cycleTheme, ["stop"]),
@@ -968,22 +884,22 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
968
884
  fill: "currentColor"
969
885
  })
970
886
  ], -1)
971
- ])], 8, _hoisted_12),
887
+ ])], 8, _hoisted_11),
972
888
  createElementVNode("button", {
973
889
  class: "action-btn collapse-btn",
974
890
  onClick: withModifiers(toggleCollapse, ["stop"]),
975
891
  title: isCollapsed.value ? "展开" : "折叠"
976
892
  }, [
977
- (openBlock(), createElementBlock("svg", _hoisted_14, [
893
+ (openBlock(), createElementBlock("svg", _hoisted_13, [
978
894
  createElementVNode("path", {
979
895
  d: isCollapsed.value ? "M18 15L12 9L6 15" : "M6 9L12 15L18 9",
980
896
  stroke: "currentColor",
981
897
  "stroke-width": "2",
982
898
  "stroke-linecap": "round",
983
899
  "stroke-linejoin": "round"
984
- }, null, 8, _hoisted_15)
900
+ }, null, 8, _hoisted_14)
985
901
  ]))
986
- ], 8, _hoisted_13),
902
+ ], 8, _hoisted_12),
987
903
  createElementVNode("button", {
988
904
  class: "action-btn minimize-btn",
989
905
  onClick: _cache[2] || (_cache[2] = withModifiers(($event) => toggleDialog(false), ["stop"])),
@@ -1016,7 +932,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1016
932
  allow: "microphone",
1017
933
  frameborder: "0",
1018
934
  onLoad: handleIframeLoad
1019
- }, null, 40, _hoisted_16)
935
+ }, null, 40, _hoisted_15)
1020
936
  ], 6)
1021
937
  ], 38)
1022
938
  ]),
@@ -1027,7 +943,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1027
943
  }
1028
944
  });
1029
945
 
1030
- const simeX = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d8f992b0"]]);
946
+ const simeX = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-73306d13"]]);
1031
947
 
1032
948
  export { _sfc_main$3 as AiChatbotProvider, simeX as AiChatbotX, AiChatbotXKey, clientCommandKey, injectStrict };
1033
949
  //# sourceMappingURL=sime-x-vue.mjs.map