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