@siact/sime-x-vue 0.0.2 → 0.0.3

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();
@@ -756,16 +678,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
756
678
  console.error("[VoiceDetector] 清理失败:", error);
757
679
  }
758
680
  }
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
681
  });
770
682
  aiChatbotX?.registerVoiceMethods({
771
683
  start: () => toggleVoiceMode(true),
@@ -810,7 +722,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
810
722
  }, null, 12, _hoisted_3),
811
723
  createVNode(Transition, { name: "indicator-fade" }, {
812
724
  default: withCtx(() => [
813
- voiceStatus.value === "listening" ? (openBlock(), createElementBlock("div", _hoisted_4, [..._cache[3] || (_cache[3] = [
725
+ voiceStatus.value === "listening" ? (openBlock(), createElementBlock("div", {
726
+ key: 0,
727
+ class: normalizeClass(["listening-badge", { "wake-active": wakeAnimating.value }])
728
+ }, [..._cache[3] || (_cache[3] = [
814
729
  createElementVNode("div", { class: "listening-waves" }, [
815
730
  createElementVNode("div", { class: "wave wave-1" }),
816
731
  createElementVNode("div", { class: "wave wave-2" }),
@@ -835,7 +750,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
835
750
  })
836
751
  ])
837
752
  ], -1)
838
- ])])) : createCommentVNode("", true)
753
+ ])], 2)) : createCommentVNode("", true)
839
754
  ]),
840
755
  _: 1
841
756
  })
@@ -869,17 +784,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
869
784
  class: "x-dialog-header",
870
785
  onMousedown: withModifiers(startDrag, ["stop"])
871
786
  }, [
872
- createElementVNode("div", _hoisted_5, [
873
- createElementVNode("div", _hoisted_6, [
787
+ createElementVNode("div", _hoisted_4, [
788
+ createElementVNode("div", _hoisted_5, [
874
789
  createElementVNode("img", {
875
790
  src: __props.xLogo ? __props.xLogo : "/sime.png",
876
791
  alt: "assistant",
877
792
  class: "logo"
878
- }, null, 8, _hoisted_7)
793
+ }, null, 8, _hoisted_6)
879
794
  ]),
880
- createElementVNode("span", _hoisted_8, toDisplayString(__props.xTitle), 1)
795
+ createElementVNode("span", _hoisted_7, toDisplayString(__props.xTitle), 1)
881
796
  ]),
882
- createElementVNode("div", _hoisted_9, [
797
+ createElementVNode("div", _hoisted_8, [
883
798
  createElementVNode("button", {
884
799
  class: "action-btn theme-btn",
885
800
  title: "开启新对话",
@@ -943,8 +858,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
943
858
  "stroke-linejoin": "round"
944
859
  })
945
860
  ], -1)),
946
- voiceStatus.value !== "standby" ? (openBlock(), createElementBlock("span", _hoisted_11)) : createCommentVNode("", true)
947
- ], 10, _hoisted_10),
861
+ voiceStatus.value !== "standby" ? (openBlock(), createElementBlock("span", _hoisted_10)) : createCommentVNode("", true)
862
+ ], 10, _hoisted_9),
948
863
  createElementVNode("button", {
949
864
  class: "action-btn theme-btn",
950
865
  onClick: withModifiers(cycleTheme, ["stop"]),
@@ -968,22 +883,22 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
968
883
  fill: "currentColor"
969
884
  })
970
885
  ], -1)
971
- ])], 8, _hoisted_12),
886
+ ])], 8, _hoisted_11),
972
887
  createElementVNode("button", {
973
888
  class: "action-btn collapse-btn",
974
889
  onClick: withModifiers(toggleCollapse, ["stop"]),
975
890
  title: isCollapsed.value ? "展开" : "折叠"
976
891
  }, [
977
- (openBlock(), createElementBlock("svg", _hoisted_14, [
892
+ (openBlock(), createElementBlock("svg", _hoisted_13, [
978
893
  createElementVNode("path", {
979
894
  d: isCollapsed.value ? "M18 15L12 9L6 15" : "M6 9L12 15L18 9",
980
895
  stroke: "currentColor",
981
896
  "stroke-width": "2",
982
897
  "stroke-linecap": "round",
983
898
  "stroke-linejoin": "round"
984
- }, null, 8, _hoisted_15)
899
+ }, null, 8, _hoisted_14)
985
900
  ]))
986
- ], 8, _hoisted_13),
901
+ ], 8, _hoisted_12),
987
902
  createElementVNode("button", {
988
903
  class: "action-btn minimize-btn",
989
904
  onClick: _cache[2] || (_cache[2] = withModifiers(($event) => toggleDialog(false), ["stop"])),
@@ -1016,7 +931,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1016
931
  allow: "microphone",
1017
932
  frameborder: "0",
1018
933
  onLoad: handleIframeLoad
1019
- }, null, 40, _hoisted_16)
934
+ }, null, 40, _hoisted_15)
1020
935
  ], 6)
1021
936
  ], 38)
1022
937
  ]),
@@ -1027,7 +942,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1027
942
  }
1028
943
  });
1029
944
 
1030
- const simeX = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d8f992b0"]]);
945
+ const simeX = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-732b3a72"]]);
1031
946
 
1032
947
  export { _sfc_main$3 as AiChatbotProvider, simeX as AiChatbotX, AiChatbotXKey, clientCommandKey, injectStrict };
1033
948
  //# sourceMappingURL=sime-x-vue.mjs.map