@yh-ui/components 0.1.12 → 0.1.15
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/ai-artifacts/src/ai-artifacts.d.ts +1 -1
- package/dist/ai-artifacts/src/ai-artifacts.d.vue.ts +18 -1
- package/dist/ai-artifacts/src/ai-artifacts.vue +30 -2
- package/dist/ai-artifacts/src/ai-artifacts.vue.d.ts +18 -1
- package/dist/ai-editor-sender/src/ai-editor-sender.d.vue.ts +2 -2
- package/dist/ai-editor-sender/src/ai-editor-sender.vue.d.ts +2 -2
- package/dist/ai-mention/__tests__/ai-mention.ssr.test.cjs +38 -0
- package/dist/ai-mention/__tests__/ai-mention.ssr.test.d.ts +1 -0
- package/dist/ai-mention/__tests__/ai-mention.ssr.test.mjs +37 -0
- package/dist/ai-mention/__tests__/ai-mention.test.cjs +113 -0
- package/dist/ai-mention/__tests__/ai-mention.test.d.ts +1 -0
- package/dist/ai-mention/__tests__/ai-mention.test.mjs +84 -0
- package/dist/ai-mention/index.cjs +26 -0
- package/dist/ai-mention/index.d.ts +29 -0
- package/dist/ai-mention/index.mjs +5 -0
- package/dist/ai-mention/src/ai-mention.cjs +95 -0
- package/dist/ai-mention/src/ai-mention.css +545 -0
- package/dist/ai-mention/src/ai-mention.d.ts +99 -0
- package/dist/ai-mention/src/ai-mention.d.vue.ts +68 -0
- package/dist/ai-mention/src/ai-mention.mjs +89 -0
- package/dist/ai-mention/src/ai-mention.vue +650 -0
- package/dist/ai-mention/src/ai-mention.vue.d.ts +68 -0
- package/dist/ai-sender/__tests__/ai-sender.test.cjs +17 -10
- package/dist/ai-sender/__tests__/ai-sender.test.mjs +17 -10
- package/dist/ai-sender/src/ai-sender.cjs +7 -0
- package/dist/ai-sender/src/ai-sender.d.ts +7 -0
- package/dist/ai-sender/src/ai-sender.d.vue.ts +8 -3
- package/dist/ai-sender/src/ai-sender.mjs +7 -0
- package/dist/ai-sender/src/ai-sender.vue +25 -18
- package/dist/ai-sender/src/ai-sender.vue.d.ts +8 -3
- package/dist/ai-sources/src/ai-sources.d.vue.ts +2 -2
- package/dist/ai-sources/src/ai-sources.vue +41 -57
- package/dist/ai-sources/src/ai-sources.vue.d.ts +2 -2
- package/dist/ai-voice-trigger/__tests__/ai-voice-trigger.test.cjs +19 -2
- package/dist/ai-voice-trigger/__tests__/ai-voice-trigger.test.mjs +12 -2
- package/dist/ai-voice-trigger/src/ai-voice-trigger.cjs +32 -0
- package/dist/ai-voice-trigger/src/ai-voice-trigger.css +111 -12
- package/dist/ai-voice-trigger/src/ai-voice-trigger.d.ts +32 -0
- package/dist/ai-voice-trigger/src/ai-voice-trigger.d.vue.ts +22 -2
- package/dist/ai-voice-trigger/src/ai-voice-trigger.mjs +32 -0
- package/dist/ai-voice-trigger/src/ai-voice-trigger.vue +201 -50
- package/dist/ai-voice-trigger/src/ai-voice-trigger.vue.d.ts +22 -2
- package/dist/alert/src/alert.d.vue.ts +1 -1
- package/dist/alert/src/alert.vue.d.ts +1 -1
- package/dist/autocomplete/src/autocomplete.d.vue.ts +2 -2
- package/dist/autocomplete/src/autocomplete.vue.d.ts +2 -2
- package/dist/calendar/src/calendar.d.vue.ts +1 -1
- package/dist/calendar/src/calendar.vue.d.ts +1 -1
- package/dist/date-picker/src/date-picker.d.vue.ts +2 -2
- package/dist/date-picker/src/date-picker.vue.d.ts +2 -2
- package/dist/dialog/src/dialog.d.vue.ts +8 -8
- package/dist/dialog/src/dialog.vue.d.ts +8 -8
- package/dist/drawer/index.d.ts +42 -1
- package/dist/drawer/src/drawer.d.vue.ts +4 -4
- package/dist/drawer/src/drawer.vue.d.ts +4 -4
- package/dist/icon/src/icons/index.cjs +7 -2
- package/dist/icon/src/icons/index.d.ts +1 -0
- package/dist/icon/src/icons/index.mjs +7 -1
- package/dist/index.cjs +13 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +4 -1
- package/dist/infinite-scroll/src/infinite-scroll.d.vue.ts +1 -1
- package/dist/infinite-scroll/src/infinite-scroll.vue.d.ts +1 -1
- package/dist/input/index.d.ts +3 -3
- package/dist/input/src/input.d.vue.ts +1 -1
- package/dist/input/src/input.vue.d.ts +1 -1
- package/dist/input-number/index.d.ts +3 -3
- package/dist/input-number/src/input-number.d.vue.ts +1 -1
- package/dist/input-number/src/input-number.vue.d.ts +1 -1
- package/dist/input-tag/src/input-tag.d.vue.ts +2 -2
- package/dist/input-tag/src/input-tag.vue.d.ts +2 -2
- package/dist/mention/index.d.ts +181 -1
- package/dist/mention/src/mention.d.vue.ts +5 -5
- package/dist/mention/src/mention.vue +6 -2
- package/dist/mention/src/mention.vue.d.ts +5 -5
- package/dist/message/src/message.d.vue.ts +1 -1
- package/dist/message/src/message.vue.d.ts +1 -1
- package/dist/notification/src/notification.d.vue.ts +1 -1
- package/dist/notification/src/notification.vue.d.ts +1 -1
- package/dist/select/src/select.d.vue.ts +3 -3
- package/dist/select/src/select.vue.d.ts +3 -3
- package/dist/skeleton/src/skeleton.d.vue.ts +1 -1
- package/dist/skeleton/src/skeleton.vue.d.ts +1 -1
- package/dist/table/src/table-column.d.vue.ts +1 -1
- package/dist/table/src/table-column.vue.d.ts +1 -1
- package/dist/table/src/table.d.vue.ts +2 -2
- package/dist/table/src/table.vue.d.ts +2 -2
- package/dist/time-picker/src/time-picker.d.vue.ts +1 -1
- package/dist/time-picker/src/time-picker.vue.d.ts +1 -1
- package/package.json +5 -5
|
@@ -18,9 +18,9 @@ describe("YhAiVoiceTrigger", () => {
|
|
|
18
18
|
});
|
|
19
19
|
it("should apply recording class when recording is true", () => {
|
|
20
20
|
const wrapper = mount(AiVoiceTrigger, {
|
|
21
|
-
props: { recording: true }
|
|
21
|
+
props: { recording: true, teleport: false }
|
|
22
22
|
});
|
|
23
|
-
expect(wrapper.classes()).toContain("is-recording");
|
|
23
|
+
expect(wrapper.find(".yh-ai-voice-trigger").classes()).toContain("is-recording");
|
|
24
24
|
expect(wrapper.find(".yh-ai-voice-trigger__visualizer").exists()).toBe(true);
|
|
25
25
|
});
|
|
26
26
|
it("should emit start and update:recording on click when initially false", async () => {
|
|
@@ -90,4 +90,14 @@ describe("YhAiVoiceTrigger", () => {
|
|
|
90
90
|
});
|
|
91
91
|
expect(wrapper.find(".yh-ai-voice-trigger__label").text()).toBe("Custom Trigger Text");
|
|
92
92
|
});
|
|
93
|
+
it("should respect teleport prop", () => {
|
|
94
|
+
const wrapper = mount(AiVoiceTrigger, {
|
|
95
|
+
props: { variant: "sphere", teleport: true }
|
|
96
|
+
});
|
|
97
|
+
expect(wrapper.props("teleport")).toBe(true);
|
|
98
|
+
const wrapperNoTeleport = mount(AiVoiceTrigger, {
|
|
99
|
+
props: { variant: "sphere", teleport: false }
|
|
100
|
+
});
|
|
101
|
+
expect(wrapperNoTeleport.props("teleport")).toBe(false);
|
|
102
|
+
});
|
|
93
103
|
});
|
|
@@ -19,6 +19,38 @@ const aiVoiceTriggerProps = exports.aiVoiceTriggerProps = {
|
|
|
19
19
|
type: Array,
|
|
20
20
|
default: () => Array(20).fill(5)
|
|
21
21
|
},
|
|
22
|
+
/**
|
|
23
|
+
* 展示模式
|
|
24
|
+
* - inline: 行内按钮
|
|
25
|
+
* - floating: 悬浮按钮
|
|
26
|
+
* - sphere: 拟物音量球
|
|
27
|
+
*/
|
|
28
|
+
variant: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: "inline"
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* 悬浮位置(仅在 floating/sphere 模式有效)
|
|
34
|
+
*/
|
|
35
|
+
position: {
|
|
36
|
+
type: String,
|
|
37
|
+
default: "bottom-right"
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* 偏移量
|
|
41
|
+
*/
|
|
42
|
+
offset: {
|
|
43
|
+
type: Array,
|
|
44
|
+
default: () => [24, 24]
|
|
45
|
+
},
|
|
46
|
+
/**
|
|
47
|
+
* 是否挂载到 body (Teleport)
|
|
48
|
+
* 仅在 floating/sphere 模式有效
|
|
49
|
+
*/
|
|
50
|
+
teleport: {
|
|
51
|
+
type: Boolean,
|
|
52
|
+
default: true
|
|
53
|
+
},
|
|
22
54
|
/**
|
|
23
55
|
* 主题覆盖变量
|
|
24
56
|
*/
|
|
@@ -470,6 +470,44 @@ html.dark {
|
|
|
470
470
|
opacity: 1;
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
|
+
@keyframes yh-sphere-pulse {
|
|
474
|
+
0% {
|
|
475
|
+
transform: scale(1);
|
|
476
|
+
opacity: 0.5;
|
|
477
|
+
}
|
|
478
|
+
100% {
|
|
479
|
+
transform: scale(3);
|
|
480
|
+
opacity: 0;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
@keyframes yh-sphere-glow {
|
|
484
|
+
0% {
|
|
485
|
+
transform: scale(1);
|
|
486
|
+
opacity: 0.4;
|
|
487
|
+
}
|
|
488
|
+
100% {
|
|
489
|
+
transform: scale(1.5);
|
|
490
|
+
opacity: 0.8;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
@keyframes yh-sphere-float {
|
|
494
|
+
0%, 100% {
|
|
495
|
+
transform: translateY(0);
|
|
496
|
+
}
|
|
497
|
+
50% {
|
|
498
|
+
transform: translateY(-10px);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
@keyframes yh-sphere-inner-pulse {
|
|
502
|
+
0% {
|
|
503
|
+
opacity: 0.7;
|
|
504
|
+
transform: scale(0.8);
|
|
505
|
+
}
|
|
506
|
+
100% {
|
|
507
|
+
opacity: 1;
|
|
508
|
+
transform: scale(1.1);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
473
511
|
.yh-voice-expand-enter-active,
|
|
474
512
|
.yh-voice-expand-leave-active {
|
|
475
513
|
transition: all 0.3s cubic-bezier(0.25, 1, 0.5, 1);
|
|
@@ -493,6 +531,65 @@ html.dark {
|
|
|
493
531
|
position: relative;
|
|
494
532
|
font-family: var(--yh-font-family);
|
|
495
533
|
}
|
|
534
|
+
.yh-ai-voice-trigger--floating {
|
|
535
|
+
z-index: 2000;
|
|
536
|
+
}
|
|
537
|
+
.yh-ai-voice-trigger--floating .yh-ai-voice-trigger__body {
|
|
538
|
+
box-shadow: var(--yh-box-shadow-dark);
|
|
539
|
+
border: 1px solid var(--yh-border-color-lighter);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.yh-ai-voice-trigger--sphere {
|
|
543
|
+
--yh-ai-voice-trigger-btn-size: 56px;
|
|
544
|
+
z-index: 2000;
|
|
545
|
+
}
|
|
546
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__body {
|
|
547
|
+
padding: 0;
|
|
548
|
+
background: transparent !important;
|
|
549
|
+
box-shadow: none !important;
|
|
550
|
+
overflow: visible;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__trigger {
|
|
554
|
+
background: var(--yh-color-primary) !important;
|
|
555
|
+
color: #fff !important;
|
|
556
|
+
box-shadow: 0 4px 12px rgba(var(--yh-color-primary-rgb), 0.4);
|
|
557
|
+
z-index: 2;
|
|
558
|
+
}
|
|
559
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__trigger.is-active {
|
|
560
|
+
animation: yh-sphere-float 3s ease-in-out infinite;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__sphere-glow {
|
|
564
|
+
position: absolute;
|
|
565
|
+
width: 100%;
|
|
566
|
+
height: 100%;
|
|
567
|
+
background: radial-gradient(circle, var(--yh-color-primary-light-3) 0%, transparent 70%);
|
|
568
|
+
filter: blur(10px);
|
|
569
|
+
opacity: 0.6;
|
|
570
|
+
animation: yh-sphere-glow 2s infinite alternate;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__sphere-pulse {
|
|
574
|
+
position: absolute;
|
|
575
|
+
top: 0;
|
|
576
|
+
left: 0;
|
|
577
|
+
width: 100%;
|
|
578
|
+
height: 100%;
|
|
579
|
+
border-radius: 50%;
|
|
580
|
+
border: 2px solid var(--yh-color-primary-light-5);
|
|
581
|
+
opacity: 0;
|
|
582
|
+
animation: yh-sphere-pulse 2s infinite;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__sphere-inner {
|
|
586
|
+
width: 20px;
|
|
587
|
+
height: 20px;
|
|
588
|
+
border-radius: 50%;
|
|
589
|
+
background: #fff;
|
|
590
|
+
animation: yh-sphere-inner-pulse 1s infinite alternate;
|
|
591
|
+
}
|
|
592
|
+
|
|
496
593
|
.yh-ai-voice-trigger__body {
|
|
497
594
|
display: flex;
|
|
498
595
|
align-items: center;
|
|
@@ -508,9 +605,6 @@ html.dark {
|
|
|
508
605
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
509
606
|
}
|
|
510
607
|
|
|
511
|
-
.yh-ai-voice-trigger {
|
|
512
|
-
/* Trigger Button */
|
|
513
|
-
}
|
|
514
608
|
.yh-ai-voice-trigger__trigger {
|
|
515
609
|
display: inline-flex;
|
|
516
610
|
align-items: center;
|
|
@@ -519,11 +613,13 @@ html.dark {
|
|
|
519
613
|
background: transparent;
|
|
520
614
|
cursor: pointer;
|
|
521
615
|
border-radius: 50%;
|
|
522
|
-
width: var(--yh-ai-voice-trigger-btn-size);
|
|
616
|
+
min-width: var(--yh-ai-voice-trigger-btn-size);
|
|
523
617
|
height: var(--yh-ai-voice-trigger-btn-size);
|
|
524
618
|
color: var(--yh-text-color-primary, #303133);
|
|
525
619
|
transition: all 0.2s;
|
|
526
620
|
flex-shrink: 0;
|
|
621
|
+
position: relative;
|
|
622
|
+
padding: 0;
|
|
527
623
|
}
|
|
528
624
|
.yh-ai-voice-trigger__trigger .yh-icon {
|
|
529
625
|
font-size: 16px;
|
|
@@ -535,22 +631,25 @@ html.dark {
|
|
|
535
631
|
}
|
|
536
632
|
.yh-ai-voice-trigger__trigger.is-active {
|
|
537
633
|
background-color: var(--yh-ai-voice-trigger-active-color);
|
|
538
|
-
color: #fff;
|
|
634
|
+
color: #fff !important;
|
|
539
635
|
animation: yh-pulse-mic 2s infinite;
|
|
540
636
|
}
|
|
541
637
|
.yh-ai-voice-trigger__trigger.is-active .yh-icon {
|
|
542
638
|
transform: scale(0.9);
|
|
543
639
|
}
|
|
544
640
|
|
|
545
|
-
.yh-ai-voice-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
641
|
+
.yh-ai-voice-trigger--inline .yh-ai-voice-trigger__trigger:not(.is-active) {
|
|
642
|
+
width: auto;
|
|
643
|
+
padding: 0 12px;
|
|
644
|
+
border-radius: 16px;
|
|
645
|
+
border: 1px solid var(--yh-border-color-lighter);
|
|
646
|
+
background-color: var(--yh-bg-color);
|
|
549
647
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
648
|
+
.yh-ai-voice-trigger--inline .yh-ai-voice-trigger__trigger:not(.is-active):hover {
|
|
649
|
+
border-color: var(--yh-color-primary);
|
|
650
|
+
background-color: var(--yh-color-primary-light-9);
|
|
553
651
|
}
|
|
652
|
+
|
|
554
653
|
.yh-ai-voice-trigger__visualizer {
|
|
555
654
|
display: flex;
|
|
556
655
|
align-items: center;
|
|
@@ -14,6 +14,38 @@ export declare const aiVoiceTriggerProps: {
|
|
|
14
14
|
readonly type: PropType<number[]>;
|
|
15
15
|
readonly default: () => any[];
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* 展示模式
|
|
19
|
+
* - inline: 行内按钮
|
|
20
|
+
* - floating: 悬浮按钮
|
|
21
|
+
* - sphere: 拟物音量球
|
|
22
|
+
*/
|
|
23
|
+
readonly variant: {
|
|
24
|
+
readonly type: PropType<"inline" | "floating" | "sphere">;
|
|
25
|
+
readonly default: "inline";
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* 悬浮位置(仅在 floating/sphere 模式有效)
|
|
29
|
+
*/
|
|
30
|
+
readonly position: {
|
|
31
|
+
readonly type: PropType<"top-left" | "top-right" | "bottom-left" | "bottom-right">;
|
|
32
|
+
readonly default: "bottom-right";
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* 偏移量
|
|
36
|
+
*/
|
|
37
|
+
readonly offset: {
|
|
38
|
+
readonly type: PropType<[number, number]>;
|
|
39
|
+
readonly default: () => number[];
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* 是否挂载到 body (Teleport)
|
|
43
|
+
* 仅在 floating/sphere 模式有效
|
|
44
|
+
*/
|
|
45
|
+
readonly teleport: {
|
|
46
|
+
readonly type: BooleanConstructor;
|
|
47
|
+
readonly default: true;
|
|
48
|
+
};
|
|
17
49
|
/**
|
|
18
50
|
* 主题覆盖变量
|
|
19
51
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
declare var
|
|
1
|
+
declare var __VLS_17: {};
|
|
2
2
|
type __VLS_Slots = {} & {
|
|
3
|
-
default?: (props: typeof
|
|
3
|
+
default?: (props: typeof __VLS_17) => any;
|
|
4
4
|
};
|
|
5
5
|
declare const __VLS_component: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
6
6
|
readonly recording: {
|
|
@@ -11,12 +11,32 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
|
|
|
11
11
|
readonly type: import("vue").PropType<number[]>;
|
|
12
12
|
readonly default: () => any[];
|
|
13
13
|
};
|
|
14
|
+
readonly variant: {
|
|
15
|
+
readonly type: import("vue").PropType<"inline" | "floating" | "sphere">;
|
|
16
|
+
readonly default: "inline";
|
|
17
|
+
};
|
|
18
|
+
readonly position: {
|
|
19
|
+
readonly type: import("vue").PropType<"top-left" | "top-right" | "bottom-left" | "bottom-right">;
|
|
20
|
+
readonly default: "bottom-right";
|
|
21
|
+
};
|
|
22
|
+
readonly offset: {
|
|
23
|
+
readonly type: import("vue").PropType<[number, number]>;
|
|
24
|
+
readonly default: () => number[];
|
|
25
|
+
};
|
|
26
|
+
readonly teleport: {
|
|
27
|
+
readonly type: BooleanConstructor;
|
|
28
|
+
readonly default: true;
|
|
29
|
+
};
|
|
14
30
|
readonly themeOverrides: {
|
|
15
31
|
readonly type: import("vue").PropType<import("@yh-ui/theme").ComponentThemeVars>;
|
|
16
32
|
readonly default: undefined;
|
|
17
33
|
};
|
|
18
34
|
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, any, string, import("vue").PublicProps, any, {
|
|
35
|
+
readonly offset: [number, number];
|
|
36
|
+
readonly position: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
19
37
|
readonly themeOverrides: import("@yh-ui/theme").ComponentThemeVars;
|
|
38
|
+
readonly variant: "inline" | "floating" | "sphere";
|
|
39
|
+
readonly teleport: boolean;
|
|
20
40
|
readonly recording: boolean;
|
|
21
41
|
readonly amplitudes: number[];
|
|
22
42
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -13,6 +13,38 @@ export const aiVoiceTriggerProps = {
|
|
|
13
13
|
type: Array,
|
|
14
14
|
default: () => Array(20).fill(5)
|
|
15
15
|
},
|
|
16
|
+
/**
|
|
17
|
+
* 展示模式
|
|
18
|
+
* - inline: 行内按钮
|
|
19
|
+
* - floating: 悬浮按钮
|
|
20
|
+
* - sphere: 拟物音量球
|
|
21
|
+
*/
|
|
22
|
+
variant: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: "inline"
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* 悬浮位置(仅在 floating/sphere 模式有效)
|
|
28
|
+
*/
|
|
29
|
+
position: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: "bottom-right"
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* 偏移量
|
|
35
|
+
*/
|
|
36
|
+
offset: {
|
|
37
|
+
type: Array,
|
|
38
|
+
default: () => [24, 24]
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* 是否挂载到 body (Teleport)
|
|
42
|
+
* 仅在 floating/sphere 模式有效
|
|
43
|
+
*/
|
|
44
|
+
teleport: {
|
|
45
|
+
type: Boolean,
|
|
46
|
+
default: true
|
|
47
|
+
},
|
|
16
48
|
/**
|
|
17
49
|
* 主题覆盖变量
|
|
18
50
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useNamespace, useLocale } from "@yh-ui/hooks";
|
|
3
3
|
import { useComponentTheme } from "@yh-ui/theme";
|
|
4
|
-
import { computed, ref, onBeforeUnmount } from "vue";
|
|
4
|
+
import { computed, ref, onBeforeUnmount, watch } from "vue";
|
|
5
5
|
import { aiVoiceTriggerProps, aiVoiceTriggerEmits } from "./ai-voice-trigger";
|
|
6
6
|
import { YhIcon } from "../../icon";
|
|
7
7
|
defineOptions({
|
|
@@ -17,12 +17,25 @@ const { themeStyle } = useComponentTheme(
|
|
|
17
17
|
);
|
|
18
18
|
const localRecording = ref(props.recording);
|
|
19
19
|
let visualizerTimer = null;
|
|
20
|
-
const simulatedAmplitudes = ref(
|
|
20
|
+
const simulatedAmplitudes = ref(Array(20).fill(5));
|
|
21
|
+
watch(
|
|
22
|
+
() => props.amplitudes,
|
|
23
|
+
(newAmps) => {
|
|
24
|
+
if (newAmps && newAmps.length > 0) {
|
|
25
|
+
const hasData = newAmps.some((a) => a > 6 || a > 0 && a !== 5);
|
|
26
|
+
if (hasData) {
|
|
27
|
+
simulatedAmplitudes.value = [...newAmps];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{ immediate: true, deep: true }
|
|
32
|
+
);
|
|
21
33
|
const syncAmplitudes = () => {
|
|
22
|
-
if (props.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
if (props.recording || localRecording.value) {
|
|
35
|
+
const hasData = props.amplitudes && props.amplitudes.some((a) => a > 6 || a > 0 && a !== 5);
|
|
36
|
+
if (!hasData) {
|
|
37
|
+
simulatedAmplitudes.value = Array.from({ length: 20 }, () => 10 + Math.random() * 40);
|
|
38
|
+
}
|
|
26
39
|
}
|
|
27
40
|
};
|
|
28
41
|
const toggleRecording = () => {
|
|
@@ -62,43 +75,82 @@ onBeforeUnmount(() => {
|
|
|
62
75
|
</script>
|
|
63
76
|
|
|
64
77
|
<template>
|
|
65
|
-
<
|
|
66
|
-
<div
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
<Teleport to="body" :disabled="props.variant === 'inline' || !props.teleport">
|
|
79
|
+
<div
|
|
80
|
+
:class="[ns.b(), ns.m(props.variant), ns.m(props.position), ns.is('recording', props.recording || localRecording)]"
|
|
81
|
+
:style="[props.variant !== 'inline' ? {
|
|
82
|
+
position: props.teleport ? 'fixed' : 'relative',
|
|
83
|
+
[props.position.split('-')[0]]: props.teleport ? props.offset[0] + 'px' : 'auto',
|
|
84
|
+
[props.position.split('-')[1]]: props.teleport ? props.offset[1] + 'px' : 'auto'
|
|
85
|
+
} : {}, themeStyle]"
|
|
86
|
+
>
|
|
87
|
+
<div :class="ns.e('body')">
|
|
88
|
+
<!-- Waveform Visualizer -->
|
|
89
|
+
<Transition name="yh-voice-expand">
|
|
90
|
+
<div v-if="props.recording || localRecording" :class="ns.e('visualizer')">
|
|
91
|
+
<!-- Sphere Visualizer (Pulsing Ball) -->
|
|
92
|
+
<template v-if="props.variant === 'sphere'">
|
|
93
|
+
<div :class="ns.e('sphere-glow')"></div>
|
|
94
|
+
<div
|
|
95
|
+
v-for="i in 3"
|
|
96
|
+
:key="i"
|
|
97
|
+
:class="ns.e('sphere-pulse')"
|
|
98
|
+
:style="{
|
|
99
|
+
animationDelay: i * 0.5 + 's',
|
|
100
|
+
transform: `scale(${1 + (simulatedAmplitudes[i] || 0) / 100})`
|
|
101
|
+
}"
|
|
102
|
+
></div>
|
|
103
|
+
</template>
|
|
104
|
+
|
|
105
|
+
<!-- Standard Wave Visualizer -->
|
|
106
|
+
<div v-else :class="ns.e('bars')">
|
|
107
|
+
<span
|
|
108
|
+
v-for="(amp, i) in simulatedAmplitudes"
|
|
109
|
+
:key="i"
|
|
110
|
+
:style="{
|
|
111
|
+
height: amp + 'px'
|
|
112
|
+
}"
|
|
113
|
+
:class="ns.e('bar')"
|
|
114
|
+
></span>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<span v-if="props.variant !== 'sphere'" :class="ns.e('hint')">{{
|
|
118
|
+
t("ai.voice.listening") || "Listening..."
|
|
119
|
+
}}</span>
|
|
120
|
+
|
|
121
|
+
<!-- Cancel Button -->
|
|
122
|
+
<button :class="ns.e('cancel')" @click="handleCancel" title="Cancel">
|
|
123
|
+
<YhIcon name="close" />
|
|
124
|
+
</button>
|
|
77
125
|
</div>
|
|
78
|
-
|
|
79
|
-
<!-- Cancel Button -->
|
|
80
|
-
<button :class="ns.e('cancel')" @click="handleCancel" title="Cancel">
|
|
81
|
-
<YhIcon name="close" />
|
|
82
|
-
</button>
|
|
83
|
-
</div>
|
|
84
|
-
</Transition>
|
|
126
|
+
</Transition>
|
|
85
127
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
128
|
+
<!-- Main Trigger Button -->
|
|
129
|
+
<button
|
|
130
|
+
:class="[ns.e('trigger'), {
|
|
89
131
|
'is-active': props.recording || localRecording
|
|
90
132
|
}]"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
133
|
+
@click="toggleRecording"
|
|
134
|
+
>
|
|
135
|
+
<span :class="ns.e('mic')">
|
|
136
|
+
<template v-if="props.variant === 'sphere' && (props.recording || localRecording)">
|
|
137
|
+
<div :class="ns.e('sphere-inner')"></div>
|
|
138
|
+
</template>
|
|
139
|
+
<YhIcon
|
|
140
|
+
v-else
|
|
141
|
+
:name="props.recording || localRecording ? 'video-pause' : 'microphone'"
|
|
142
|
+
/>
|
|
143
|
+
</span>
|
|
144
|
+
<span
|
|
145
|
+
v-if="!(props.recording || localRecording) && props.variant === 'inline'"
|
|
146
|
+
:class="ns.e('label')"
|
|
147
|
+
>
|
|
148
|
+
<slot>{{ t("ai.voice.trigger") || "" }}</slot>
|
|
149
|
+
</span>
|
|
150
|
+
</button>
|
|
151
|
+
</div>
|
|
100
152
|
</div>
|
|
101
|
-
</
|
|
153
|
+
</Teleport>
|
|
102
154
|
</template>
|
|
103
155
|
|
|
104
156
|
<style>
|
|
@@ -574,6 +626,44 @@ html.dark {
|
|
|
574
626
|
opacity: 1;
|
|
575
627
|
}
|
|
576
628
|
}
|
|
629
|
+
@keyframes yh-sphere-pulse {
|
|
630
|
+
0% {
|
|
631
|
+
transform: scale(1);
|
|
632
|
+
opacity: 0.5;
|
|
633
|
+
}
|
|
634
|
+
100% {
|
|
635
|
+
transform: scale(3);
|
|
636
|
+
opacity: 0;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
@keyframes yh-sphere-glow {
|
|
640
|
+
0% {
|
|
641
|
+
transform: scale(1);
|
|
642
|
+
opacity: 0.4;
|
|
643
|
+
}
|
|
644
|
+
100% {
|
|
645
|
+
transform: scale(1.5);
|
|
646
|
+
opacity: 0.8;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
@keyframes yh-sphere-float {
|
|
650
|
+
0%, 100% {
|
|
651
|
+
transform: translateY(0);
|
|
652
|
+
}
|
|
653
|
+
50% {
|
|
654
|
+
transform: translateY(-10px);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
@keyframes yh-sphere-inner-pulse {
|
|
658
|
+
0% {
|
|
659
|
+
opacity: 0.7;
|
|
660
|
+
transform: scale(0.8);
|
|
661
|
+
}
|
|
662
|
+
100% {
|
|
663
|
+
opacity: 1;
|
|
664
|
+
transform: scale(1.1);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
577
667
|
.yh-voice-expand-enter-active,
|
|
578
668
|
.yh-voice-expand-leave-active {
|
|
579
669
|
transition: all 0.3s cubic-bezier(0.25, 1, 0.5, 1);
|
|
@@ -597,6 +687,65 @@ html.dark {
|
|
|
597
687
|
position: relative;
|
|
598
688
|
font-family: var(--yh-font-family);
|
|
599
689
|
}
|
|
690
|
+
.yh-ai-voice-trigger--floating {
|
|
691
|
+
z-index: 2000;
|
|
692
|
+
}
|
|
693
|
+
.yh-ai-voice-trigger--floating .yh-ai-voice-trigger__body {
|
|
694
|
+
box-shadow: var(--yh-box-shadow-dark);
|
|
695
|
+
border: 1px solid var(--yh-border-color-lighter);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.yh-ai-voice-trigger--sphere {
|
|
699
|
+
--yh-ai-voice-trigger-btn-size: 56px;
|
|
700
|
+
z-index: 2000;
|
|
701
|
+
}
|
|
702
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__body {
|
|
703
|
+
padding: 0;
|
|
704
|
+
background: transparent !important;
|
|
705
|
+
box-shadow: none !important;
|
|
706
|
+
overflow: visible;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__trigger {
|
|
710
|
+
background: var(--yh-color-primary) !important;
|
|
711
|
+
color: #fff !important;
|
|
712
|
+
box-shadow: 0 4px 12px rgba(var(--yh-color-primary-rgb), 0.4);
|
|
713
|
+
z-index: 2;
|
|
714
|
+
}
|
|
715
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__trigger.is-active {
|
|
716
|
+
animation: yh-sphere-float 3s ease-in-out infinite;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__sphere-glow {
|
|
720
|
+
position: absolute;
|
|
721
|
+
width: 100%;
|
|
722
|
+
height: 100%;
|
|
723
|
+
background: radial-gradient(circle, var(--yh-color-primary-light-3) 0%, transparent 70%);
|
|
724
|
+
filter: blur(10px);
|
|
725
|
+
opacity: 0.6;
|
|
726
|
+
animation: yh-sphere-glow 2s infinite alternate;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__sphere-pulse {
|
|
730
|
+
position: absolute;
|
|
731
|
+
top: 0;
|
|
732
|
+
left: 0;
|
|
733
|
+
width: 100%;
|
|
734
|
+
height: 100%;
|
|
735
|
+
border-radius: 50%;
|
|
736
|
+
border: 2px solid var(--yh-color-primary-light-5);
|
|
737
|
+
opacity: 0;
|
|
738
|
+
animation: yh-sphere-pulse 2s infinite;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.yh-ai-voice-trigger--sphere .yh-ai-voice-trigger__sphere-inner {
|
|
742
|
+
width: 20px;
|
|
743
|
+
height: 20px;
|
|
744
|
+
border-radius: 50%;
|
|
745
|
+
background: #fff;
|
|
746
|
+
animation: yh-sphere-inner-pulse 1s infinite alternate;
|
|
747
|
+
}
|
|
748
|
+
|
|
600
749
|
.yh-ai-voice-trigger__body {
|
|
601
750
|
display: flex;
|
|
602
751
|
align-items: center;
|
|
@@ -612,9 +761,6 @@ html.dark {
|
|
|
612
761
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
613
762
|
}
|
|
614
763
|
|
|
615
|
-
.yh-ai-voice-trigger {
|
|
616
|
-
/* Trigger Button */
|
|
617
|
-
}
|
|
618
764
|
.yh-ai-voice-trigger__trigger {
|
|
619
765
|
display: inline-flex;
|
|
620
766
|
align-items: center;
|
|
@@ -623,11 +769,13 @@ html.dark {
|
|
|
623
769
|
background: transparent;
|
|
624
770
|
cursor: pointer;
|
|
625
771
|
border-radius: 50%;
|
|
626
|
-
width: var(--yh-ai-voice-trigger-btn-size);
|
|
772
|
+
min-width: var(--yh-ai-voice-trigger-btn-size);
|
|
627
773
|
height: var(--yh-ai-voice-trigger-btn-size);
|
|
628
774
|
color: var(--yh-text-color-primary, #303133);
|
|
629
775
|
transition: all 0.2s;
|
|
630
776
|
flex-shrink: 0;
|
|
777
|
+
position: relative;
|
|
778
|
+
padding: 0;
|
|
631
779
|
}
|
|
632
780
|
.yh-ai-voice-trigger__trigger .yh-icon {
|
|
633
781
|
font-size: 16px;
|
|
@@ -639,22 +787,25 @@ html.dark {
|
|
|
639
787
|
}
|
|
640
788
|
.yh-ai-voice-trigger__trigger.is-active {
|
|
641
789
|
background-color: var(--yh-ai-voice-trigger-active-color);
|
|
642
|
-
color: #fff;
|
|
790
|
+
color: #fff !important;
|
|
643
791
|
animation: yh-pulse-mic 2s infinite;
|
|
644
792
|
}
|
|
645
793
|
.yh-ai-voice-trigger__trigger.is-active .yh-icon {
|
|
646
794
|
transform: scale(0.9);
|
|
647
795
|
}
|
|
648
796
|
|
|
649
|
-
.yh-ai-voice-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
797
|
+
.yh-ai-voice-trigger--inline .yh-ai-voice-trigger__trigger:not(.is-active) {
|
|
798
|
+
width: auto;
|
|
799
|
+
padding: 0 12px;
|
|
800
|
+
border-radius: 16px;
|
|
801
|
+
border: 1px solid var(--yh-border-color-lighter);
|
|
802
|
+
background-color: var(--yh-bg-color);
|
|
653
803
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
804
|
+
.yh-ai-voice-trigger--inline .yh-ai-voice-trigger__trigger:not(.is-active):hover {
|
|
805
|
+
border-color: var(--yh-color-primary);
|
|
806
|
+
background-color: var(--yh-color-primary-light-9);
|
|
657
807
|
}
|
|
808
|
+
|
|
658
809
|
.yh-ai-voice-trigger__visualizer {
|
|
659
810
|
display: flex;
|
|
660
811
|
align-items: center;
|