@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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type AiMentionOption } from './ai-mention';
|
|
2
|
+
declare var __VLS_22: any, __VLS_23: any;
|
|
3
|
+
type __VLS_Slots = {} & {
|
|
4
|
+
[K in NonNullable<typeof __VLS_22>]?: (props: typeof __VLS_23) => any;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_component: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
7
|
+
readonly modelValue: {
|
|
8
|
+
readonly type: StringConstructor;
|
|
9
|
+
readonly default: "";
|
|
10
|
+
};
|
|
11
|
+
readonly types: {
|
|
12
|
+
readonly type: import("vue").PropType<import("./ai-mention").AiMentionType[]>;
|
|
13
|
+
readonly default: () => string[];
|
|
14
|
+
};
|
|
15
|
+
readonly options: {
|
|
16
|
+
readonly type: import("vue").PropType<AiMentionOption[]>;
|
|
17
|
+
readonly default: () => never[];
|
|
18
|
+
};
|
|
19
|
+
readonly triggers: {
|
|
20
|
+
readonly type: import("vue").PropType<string[]>;
|
|
21
|
+
readonly default: () => string[];
|
|
22
|
+
};
|
|
23
|
+
readonly type: {
|
|
24
|
+
readonly type: import("vue").PropType<"input" | "textarea">;
|
|
25
|
+
readonly default: "textarea";
|
|
26
|
+
};
|
|
27
|
+
readonly placeholder: StringConstructor;
|
|
28
|
+
readonly disabled: BooleanConstructor;
|
|
29
|
+
readonly size: import("vue").PropType<"large" | "default" | "small">;
|
|
30
|
+
readonly maxLength: NumberConstructor;
|
|
31
|
+
readonly rows: {
|
|
32
|
+
readonly type: NumberConstructor;
|
|
33
|
+
readonly default: 3;
|
|
34
|
+
};
|
|
35
|
+
readonly loading: BooleanConstructor;
|
|
36
|
+
readonly themeOverrides: {
|
|
37
|
+
readonly type: import("vue").PropType<import("@yh-ui/theme").ComponentThemeVars>;
|
|
38
|
+
readonly default: () => {};
|
|
39
|
+
};
|
|
40
|
+
readonly filterOption: {
|
|
41
|
+
readonly type: import("vue").PropType<((keyword: string, option: import("../../mention").MentionOption) => boolean) | false>;
|
|
42
|
+
readonly default: undefined;
|
|
43
|
+
};
|
|
44
|
+
}>, {
|
|
45
|
+
focus: () => void | undefined;
|
|
46
|
+
blur: () => void | undefined;
|
|
47
|
+
clear: () => void | undefined;
|
|
48
|
+
getRef: () => HTMLTextAreaElement | HTMLInputElement | undefined;
|
|
49
|
+
insertMention: (option: AiMentionOption, trigger?: string) => void | undefined;
|
|
50
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, any, string, import("vue").PublicProps, any, {
|
|
51
|
+
readonly disabled: boolean;
|
|
52
|
+
readonly themeOverrides: import("@yh-ui/theme").ComponentThemeVars;
|
|
53
|
+
readonly type: "textarea" | "input";
|
|
54
|
+
readonly loading: boolean;
|
|
55
|
+
readonly triggers: string[];
|
|
56
|
+
readonly options: AiMentionOption[];
|
|
57
|
+
readonly modelValue: string;
|
|
58
|
+
readonly filterOption: false | ((keyword: string, option: import("../../mention").MentionOption) => boolean);
|
|
59
|
+
readonly rows: number;
|
|
60
|
+
readonly types: ("table" | "document" | "agent" | "knowledge")[];
|
|
61
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
62
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
63
|
+
export default _default;
|
|
64
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
65
|
+
new (): {
|
|
66
|
+
$slots: S;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
@@ -6,6 +6,12 @@ var _vue = require("vue");
|
|
|
6
6
|
var _aiSender = _interopRequireDefault(require("../src/ai-sender.vue"));
|
|
7
7
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
8
|
(0, _vitest.describe)("YhAiSender", () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
_vitest.vi.useFakeTimers();
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
_vitest.vi.useRealTimers();
|
|
14
|
+
});
|
|
9
15
|
(0, _vitest.it)("should render with base class", () => {
|
|
10
16
|
const wrapper = (0, _testUtils.mount)(_aiSender.default);
|
|
11
17
|
(0, _vitest.expect)(wrapper.find(".yh-ai-sender").exists()).toBe(true);
|
|
@@ -52,7 +58,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
52
58
|
disabled: true
|
|
53
59
|
}
|
|
54
60
|
});
|
|
55
|
-
(0, _vitest.expect)(wrapper.classes()).toContain("is-disabled");
|
|
61
|
+
(0, _vitest.expect)(wrapper.find(".yh-ai-sender").classes()).toContain("is-disabled");
|
|
56
62
|
});
|
|
57
63
|
(0, _vitest.it)("should disable textarea when disabled=true", () => {
|
|
58
64
|
const wrapper = (0, _testUtils.mount)(_aiSender.default, {
|
|
@@ -68,7 +74,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
68
74
|
loading: true
|
|
69
75
|
}
|
|
70
76
|
});
|
|
71
|
-
(0, _vitest.expect)(wrapper.classes()).toContain("is-loading");
|
|
77
|
+
(0, _vitest.expect)(wrapper.find(".yh-ai-sender").classes()).toContain("is-loading");
|
|
72
78
|
});
|
|
73
79
|
(0, _vitest.it)("should disable textarea when loading=true", () => {
|
|
74
80
|
const wrapper = (0, _testUtils.mount)(_aiSender.default, {
|
|
@@ -194,13 +200,15 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
194
200
|
(0, _vitest.it)("should apply is-focused on textarea focus", async () => {
|
|
195
201
|
const wrapper = (0, _testUtils.mount)(_aiSender.default);
|
|
196
202
|
await wrapper.find("textarea").trigger("focus");
|
|
197
|
-
(0, _vitest.expect)(wrapper.classes()).toContain("is-focused");
|
|
203
|
+
(0, _vitest.expect)(wrapper.find(".yh-ai-sender").classes()).toContain("is-focused");
|
|
198
204
|
});
|
|
199
205
|
(0, _vitest.it)("should remove is-focused on textarea blur", async () => {
|
|
200
206
|
const wrapper = (0, _testUtils.mount)(_aiSender.default);
|
|
201
|
-
await wrapper.
|
|
202
|
-
await wrapper.
|
|
203
|
-
|
|
207
|
+
await wrapper.get("textarea").trigger("focus");
|
|
208
|
+
await wrapper.get("textarea").trigger("blur");
|
|
209
|
+
_vitest.vi.advanceTimersByTime(400);
|
|
210
|
+
await (0, _vue.nextTick)();
|
|
211
|
+
(0, _vitest.expect)(wrapper.find(".yh-ai-sender").classes()).not.toContain("is-focused");
|
|
204
212
|
});
|
|
205
213
|
(0, _vitest.it)("should render prefix slot", () => {
|
|
206
214
|
const wrapper = (0, _testUtils.mount)(_aiSender.default, {
|
|
@@ -253,10 +261,11 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
253
261
|
commands
|
|
254
262
|
}
|
|
255
263
|
});
|
|
256
|
-
const textarea = wrapper.
|
|
264
|
+
const textarea = wrapper.get("textarea");
|
|
257
265
|
await textarea.setValue("/h");
|
|
258
266
|
textarea.element.selectionStart = 2;
|
|
259
267
|
await textarea.trigger("input");
|
|
268
|
+
await (0, _vue.nextTick)();
|
|
260
269
|
(0, _vitest.expect)(wrapper.find(".yh-ai-sender__command-panel").exists()).toBe(true);
|
|
261
270
|
(0, _vitest.expect)(wrapper.findAll(".yh-ai-sender__command-item").length).toBe(1);
|
|
262
271
|
});
|
|
@@ -312,7 +321,6 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
312
321
|
(0, _vitest.expect)(wrapper.find(".yh-ai-sender__command-panel").exists()).toBe(false);
|
|
313
322
|
});
|
|
314
323
|
(0, _vitest.it)("should hide commands on blur after delay", async () => {
|
|
315
|
-
_vitest.vi.useFakeTimers();
|
|
316
324
|
const wrapper = (0, _testUtils.mount)(_aiSender.default, {
|
|
317
325
|
props: {
|
|
318
326
|
commands
|
|
@@ -323,10 +331,9 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
323
331
|
textarea.element.selectionStart = 1;
|
|
324
332
|
await textarea.trigger("input");
|
|
325
333
|
await textarea.trigger("blur");
|
|
326
|
-
_vitest.vi.advanceTimersByTime(
|
|
334
|
+
_vitest.vi.advanceTimersByTime(400);
|
|
327
335
|
await (0, _vue.nextTick)();
|
|
328
336
|
(0, _vitest.expect)(wrapper.find(".yh-ai-sender__command-panel").exists()).toBe(false);
|
|
329
|
-
_vitest.vi.useRealTimers();
|
|
330
337
|
});
|
|
331
338
|
});
|
|
332
339
|
(0, _vitest.describe)("Attachments", () => {
|
|
@@ -3,6 +3,12 @@ import { mount } from "@vue/test-utils";
|
|
|
3
3
|
import { nextTick } from "vue";
|
|
4
4
|
import AiSender from "../src/ai-sender.vue";
|
|
5
5
|
describe("YhAiSender", () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.useFakeTimers();
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.useRealTimers();
|
|
11
|
+
});
|
|
6
12
|
it("should render with base class", () => {
|
|
7
13
|
const wrapper = mount(AiSender);
|
|
8
14
|
expect(wrapper.find(".yh-ai-sender").exists()).toBe(true);
|
|
@@ -33,7 +39,7 @@ describe("YhAiSender", () => {
|
|
|
33
39
|
});
|
|
34
40
|
it("should apply is-disabled class when disabled=true", () => {
|
|
35
41
|
const wrapper = mount(AiSender, { props: { disabled: true } });
|
|
36
|
-
expect(wrapper.classes()).toContain("is-disabled");
|
|
42
|
+
expect(wrapper.find(".yh-ai-sender").classes()).toContain("is-disabled");
|
|
37
43
|
});
|
|
38
44
|
it("should disable textarea when disabled=true", () => {
|
|
39
45
|
const wrapper = mount(AiSender, { props: { disabled: true } });
|
|
@@ -41,7 +47,7 @@ describe("YhAiSender", () => {
|
|
|
41
47
|
});
|
|
42
48
|
it("should apply is-loading class when loading=true", () => {
|
|
43
49
|
const wrapper = mount(AiSender, { props: { loading: true } });
|
|
44
|
-
expect(wrapper.classes()).toContain("is-loading");
|
|
50
|
+
expect(wrapper.find(".yh-ai-sender").classes()).toContain("is-loading");
|
|
45
51
|
});
|
|
46
52
|
it("should disable textarea when loading=true", () => {
|
|
47
53
|
const wrapper = mount(AiSender, { props: { loading: true } });
|
|
@@ -114,13 +120,15 @@ describe("YhAiSender", () => {
|
|
|
114
120
|
it("should apply is-focused on textarea focus", async () => {
|
|
115
121
|
const wrapper = mount(AiSender);
|
|
116
122
|
await wrapper.find("textarea").trigger("focus");
|
|
117
|
-
expect(wrapper.classes()).toContain("is-focused");
|
|
123
|
+
expect(wrapper.find(".yh-ai-sender").classes()).toContain("is-focused");
|
|
118
124
|
});
|
|
119
125
|
it("should remove is-focused on textarea blur", async () => {
|
|
120
126
|
const wrapper = mount(AiSender);
|
|
121
|
-
await wrapper.
|
|
122
|
-
await wrapper.
|
|
123
|
-
|
|
127
|
+
await wrapper.get("textarea").trigger("focus");
|
|
128
|
+
await wrapper.get("textarea").trigger("blur");
|
|
129
|
+
vi.advanceTimersByTime(400);
|
|
130
|
+
await nextTick();
|
|
131
|
+
expect(wrapper.find(".yh-ai-sender").classes()).not.toContain("is-focused");
|
|
124
132
|
});
|
|
125
133
|
it("should render prefix slot", () => {
|
|
126
134
|
const wrapper = mount(AiSender, {
|
|
@@ -153,10 +161,11 @@ describe("YhAiSender", () => {
|
|
|
153
161
|
];
|
|
154
162
|
it("should show slash commands panel when triggering /", async () => {
|
|
155
163
|
const wrapper = mount(AiSender, { props: { commands } });
|
|
156
|
-
const textarea = wrapper.
|
|
164
|
+
const textarea = wrapper.get("textarea");
|
|
157
165
|
await textarea.setValue("/h");
|
|
158
166
|
textarea.element.selectionStart = 2;
|
|
159
167
|
await textarea.trigger("input");
|
|
168
|
+
await nextTick();
|
|
160
169
|
expect(wrapper.find(".yh-ai-sender__command-panel").exists()).toBe(true);
|
|
161
170
|
expect(wrapper.findAll(".yh-ai-sender__command-item").length).toBe(1);
|
|
162
171
|
});
|
|
@@ -192,17 +201,15 @@ describe("YhAiSender", () => {
|
|
|
192
201
|
expect(wrapper.find(".yh-ai-sender__command-panel").exists()).toBe(false);
|
|
193
202
|
});
|
|
194
203
|
it("should hide commands on blur after delay", async () => {
|
|
195
|
-
vi.useFakeTimers();
|
|
196
204
|
const wrapper = mount(AiSender, { props: { commands } });
|
|
197
205
|
const textarea = wrapper.find("textarea");
|
|
198
206
|
await textarea.setValue("/");
|
|
199
207
|
textarea.element.selectionStart = 1;
|
|
200
208
|
await textarea.trigger("input");
|
|
201
209
|
await textarea.trigger("blur");
|
|
202
|
-
vi.advanceTimersByTime(
|
|
210
|
+
vi.advanceTimersByTime(400);
|
|
203
211
|
await nextTick();
|
|
204
212
|
expect(wrapper.find(".yh-ai-sender__command-panel").exists()).toBe(false);
|
|
205
|
-
vi.useRealTimers();
|
|
206
213
|
});
|
|
207
214
|
});
|
|
208
215
|
describe("Attachments", () => {
|
|
@@ -105,6 +105,13 @@ export declare const aiSenderProps: {
|
|
|
105
105
|
readonly type: PropType<AiCommand[]>;
|
|
106
106
|
readonly default: () => never[];
|
|
107
107
|
};
|
|
108
|
+
/**
|
|
109
|
+
* AI 提及配置
|
|
110
|
+
*/
|
|
111
|
+
readonly mentionOptions: {
|
|
112
|
+
readonly type: PropType<import("../../ai-mention").AiMentionOption[]>;
|
|
113
|
+
readonly default: () => never[];
|
|
114
|
+
};
|
|
108
115
|
/**
|
|
109
116
|
* 已选附件列表
|
|
110
117
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AiCommand } from './ai-sender';
|
|
2
|
-
declare var __VLS_9: {},
|
|
2
|
+
declare var __VLS_9: {}, __VLS_36: {}, __VLS_50: {
|
|
3
3
|
disabled: any;
|
|
4
4
|
loading: any;
|
|
5
5
|
submit: any;
|
|
@@ -7,9 +7,9 @@ declare var __VLS_9: {}, __VLS_23: {}, __VLS_37: {
|
|
|
7
7
|
type __VLS_Slots = {} & {
|
|
8
8
|
prefix?: (props: typeof __VLS_9) => any;
|
|
9
9
|
} & {
|
|
10
|
-
actions?: (props: typeof
|
|
10
|
+
actions?: (props: typeof __VLS_36) => any;
|
|
11
11
|
} & {
|
|
12
|
-
submit?: (props: typeof
|
|
12
|
+
submit?: (props: typeof __VLS_50) => any;
|
|
13
13
|
};
|
|
14
14
|
declare const __VLS_component: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
15
15
|
readonly modelValue: {
|
|
@@ -41,6 +41,10 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
|
|
|
41
41
|
readonly type: import("vue").PropType<AiCommand[]>;
|
|
42
42
|
readonly default: () => never[];
|
|
43
43
|
};
|
|
44
|
+
readonly mentionOptions: {
|
|
45
|
+
readonly type: import("vue").PropType<import("../../ai-mention").AiMentionOption[]>;
|
|
46
|
+
readonly default: () => never[];
|
|
47
|
+
};
|
|
44
48
|
readonly attachments: {
|
|
45
49
|
readonly type: import("vue").PropType<import("./ai-sender").AiAttachment[]>;
|
|
46
50
|
readonly default: () => never[];
|
|
@@ -58,6 +62,7 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
|
|
|
58
62
|
readonly clearable: boolean;
|
|
59
63
|
readonly showWordLimit: boolean;
|
|
60
64
|
readonly commands: AiCommand[];
|
|
65
|
+
readonly mentionOptions: import("../../ai-mention").AiMentionOption[];
|
|
61
66
|
readonly attachments: import("./ai-sender").AiAttachment[];
|
|
62
67
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
63
68
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
@@ -5,6 +5,7 @@ import { aiSenderProps, aiSenderEmits } from "./ai-sender";
|
|
|
5
5
|
import { YhButton } from "../../button";
|
|
6
6
|
import { YhIcon } from "../../icon";
|
|
7
7
|
import { YhImage } from "../../image";
|
|
8
|
+
import { YhAiMention } from "../../ai-mention";
|
|
8
9
|
import { useComponentTheme } from "@yh-ui/theme";
|
|
9
10
|
defineOptions({
|
|
10
11
|
name: "YhAiSender"
|
|
@@ -17,8 +18,8 @@ const { themeStyle } = useComponentTheme(
|
|
|
17
18
|
"ai-sender",
|
|
18
19
|
computed(() => props.themeOverrides)
|
|
19
20
|
);
|
|
20
|
-
const textareaRef = ref();
|
|
21
|
-
const localValue = ref(props.modelValue);
|
|
21
|
+
const textareaRef = ref(null);
|
|
22
|
+
const localValue = ref(props.modelValue ?? "");
|
|
22
23
|
const isFocused = ref(false);
|
|
23
24
|
const showCommands = ref(false);
|
|
24
25
|
const commandSearch = ref("");
|
|
@@ -46,15 +47,16 @@ const filteredCommands = computed(() => {
|
|
|
46
47
|
);
|
|
47
48
|
});
|
|
48
49
|
const autoResize = () => {
|
|
49
|
-
const el = textareaRef.value;
|
|
50
|
+
const el = textareaRef.value?.getRef();
|
|
50
51
|
if (!el) return;
|
|
51
52
|
el.style.height = "auto";
|
|
52
53
|
el.style.height = `${el.scrollHeight}px`;
|
|
53
54
|
};
|
|
54
|
-
const handleInput = (
|
|
55
|
-
const val = e.target.value;
|
|
55
|
+
const handleInput = (val) => {
|
|
56
56
|
innerValue.value = val;
|
|
57
|
-
const
|
|
57
|
+
const el = textareaRef.value?.getRef();
|
|
58
|
+
if (!el) return;
|
|
59
|
+
const cursorPosition = el.selectionStart || 0;
|
|
58
60
|
const textBeforeCursor = val.slice(0, cursorPosition);
|
|
59
61
|
const lastSlashIndex = textBeforeCursor.lastIndexOf("/");
|
|
60
62
|
if (lastSlashIndex !== -1) {
|
|
@@ -77,7 +79,8 @@ const handleInput = (e) => {
|
|
|
77
79
|
};
|
|
78
80
|
const handleSelectCommand = (command) => {
|
|
79
81
|
const val = innerValue.value;
|
|
80
|
-
const
|
|
82
|
+
const el = textareaRef.value?.getRef();
|
|
83
|
+
const cursorPosition = el?.selectionStart || 0;
|
|
81
84
|
const textBeforeCursor = val.slice(0, cursorPosition);
|
|
82
85
|
const lastSlashIndex = textBeforeCursor.lastIndexOf("/");
|
|
83
86
|
const textAfterCursor = val.slice(cursorPosition);
|
|
@@ -107,7 +110,7 @@ const handleKeyDown = (e) => {
|
|
|
107
110
|
return;
|
|
108
111
|
}
|
|
109
112
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
110
|
-
if (!innerValue.value
|
|
113
|
+
if (!innerValue.value?.trim() || props.loading || props.disabled) {
|
|
111
114
|
e.preventDefault();
|
|
112
115
|
} else {
|
|
113
116
|
e.preventDefault();
|
|
@@ -116,12 +119,13 @@ const handleKeyDown = (e) => {
|
|
|
116
119
|
}
|
|
117
120
|
};
|
|
118
121
|
const handleSend = () => {
|
|
119
|
-
if (!innerValue.value
|
|
122
|
+
if (!innerValue.value?.trim() || props.loading || props.disabled) return;
|
|
120
123
|
emit("send", innerValue.value);
|
|
121
124
|
innerValue.value = "";
|
|
122
125
|
nextTick(() => {
|
|
123
|
-
|
|
124
|
-
|
|
126
|
+
const el = textareaRef.value?.getRef();
|
|
127
|
+
if (el) {
|
|
128
|
+
el.style.height = "auto";
|
|
125
129
|
}
|
|
126
130
|
});
|
|
127
131
|
};
|
|
@@ -214,19 +218,22 @@ const handleFocus = (e) => {
|
|
|
214
218
|
</div>
|
|
215
219
|
|
|
216
220
|
<div :class="ns.e('textarea-container')">
|
|
217
|
-
<
|
|
221
|
+
<YhAiMention
|
|
218
222
|
ref="textareaRef"
|
|
219
223
|
v-model="innerValue"
|
|
224
|
+
type="textarea"
|
|
220
225
|
:class="ns.e('textarea')"
|
|
221
226
|
:placeholder="placeholder === 'Send a message...' ? t('ai.sender.placeholder') : placeholder"
|
|
222
227
|
:disabled="disabled || loading"
|
|
223
|
-
:
|
|
224
|
-
rows="1"
|
|
228
|
+
:max-length="maxLength"
|
|
229
|
+
:rows="1"
|
|
230
|
+
:options="mentionOptions"
|
|
231
|
+
:trigger="['@', '#']"
|
|
225
232
|
@focus="handleFocus"
|
|
226
233
|
@blur="handleBlur"
|
|
227
234
|
@input="handleInput"
|
|
228
235
|
@keydown="handleKeyDown"
|
|
229
|
-
|
|
236
|
+
/>
|
|
230
237
|
</div>
|
|
231
238
|
|
|
232
239
|
<div :class="ns.e('suffix')">
|
|
@@ -252,14 +259,14 @@ const handleFocus = (e) => {
|
|
|
252
259
|
|
|
253
260
|
<slot
|
|
254
261
|
name="submit"
|
|
255
|
-
:disabled="!innerValue
|
|
262
|
+
:disabled="!innerValue?.trim() || disabled"
|
|
256
263
|
:loading="loading"
|
|
257
264
|
:submit="handleSend"
|
|
258
265
|
>
|
|
259
266
|
<YhButton
|
|
260
|
-
:type="innerValue
|
|
267
|
+
:type="innerValue?.trim() && !disabled && !loading ? 'primary' : 'default'"
|
|
261
268
|
:class="ns.e('send-btn')"
|
|
262
|
-
:disabled="!innerValue
|
|
269
|
+
:disabled="!innerValue?.trim() || disabled"
|
|
263
270
|
:loading="loading"
|
|
264
271
|
@click="handleSend"
|
|
265
272
|
circle
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AiCommand } from './ai-sender';
|
|
2
|
-
declare var __VLS_9: {},
|
|
2
|
+
declare var __VLS_9: {}, __VLS_36: {}, __VLS_50: {
|
|
3
3
|
disabled: any;
|
|
4
4
|
loading: any;
|
|
5
5
|
submit: any;
|
|
@@ -7,9 +7,9 @@ declare var __VLS_9: {}, __VLS_23: {}, __VLS_37: {
|
|
|
7
7
|
type __VLS_Slots = {} & {
|
|
8
8
|
prefix?: (props: typeof __VLS_9) => any;
|
|
9
9
|
} & {
|
|
10
|
-
actions?: (props: typeof
|
|
10
|
+
actions?: (props: typeof __VLS_36) => any;
|
|
11
11
|
} & {
|
|
12
|
-
submit?: (props: typeof
|
|
12
|
+
submit?: (props: typeof __VLS_50) => any;
|
|
13
13
|
};
|
|
14
14
|
declare const __VLS_component: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
15
15
|
readonly modelValue: {
|
|
@@ -41,6 +41,10 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
|
|
|
41
41
|
readonly type: import("vue").PropType<AiCommand[]>;
|
|
42
42
|
readonly default: () => never[];
|
|
43
43
|
};
|
|
44
|
+
readonly mentionOptions: {
|
|
45
|
+
readonly type: import("vue").PropType<import("../../ai-mention").AiMentionOption[]>;
|
|
46
|
+
readonly default: () => never[];
|
|
47
|
+
};
|
|
44
48
|
readonly attachments: {
|
|
45
49
|
readonly type: import("vue").PropType<import("./ai-sender").AiAttachment[]>;
|
|
46
50
|
readonly default: () => never[];
|
|
@@ -58,6 +62,7 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
|
|
|
58
62
|
readonly clearable: boolean;
|
|
59
63
|
readonly showWordLimit: boolean;
|
|
60
64
|
readonly commands: AiCommand[];
|
|
65
|
+
readonly mentionOptions: import("../../ai-mention").AiMentionOption[];
|
|
61
66
|
readonly attachments: import("./ai-sender").AiAttachment[];
|
|
62
67
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
63
68
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AiSourceItem } from './ai-sources';
|
|
2
|
-
declare var
|
|
2
|
+
declare var __VLS_65: {};
|
|
3
3
|
type __VLS_Slots = {} & {
|
|
4
|
-
default?: (props: typeof
|
|
4
|
+
default?: (props: typeof __VLS_65) => any;
|
|
5
5
|
};
|
|
6
6
|
declare const __VLS_component: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
7
7
|
readonly sources: {
|
|
@@ -4,6 +4,7 @@ import { ref, computed } from "vue";
|
|
|
4
4
|
import { aiSourcesProps, aiSourcesEmits } from "./ai-sources";
|
|
5
5
|
import { YhIcon } from "../../icon";
|
|
6
6
|
import { YhTooltip } from "../../tooltip";
|
|
7
|
+
import { YhDrawer } from "../../drawer";
|
|
7
8
|
import { useComponentTheme } from "@yh-ui/theme";
|
|
8
9
|
defineOptions({
|
|
9
10
|
name: "YhAiSources"
|
|
@@ -76,69 +77,52 @@ const openDrawer = (source) => {
|
|
|
76
77
|
</div>
|
|
77
78
|
|
|
78
79
|
<!-- 抽屉:来源详情 -->
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
:class="ns.e('score-badge')"
|
|
110
|
-
:style="{
|
|
80
|
+
<YhDrawer
|
|
81
|
+
v-model="drawerVisible"
|
|
82
|
+
:title="t('ai.sources.drawerTitle') || '\u53C2\u8003\u6765\u6E90'"
|
|
83
|
+
size="40%"
|
|
84
|
+
:theme-overrides="themeOverrides"
|
|
85
|
+
>
|
|
86
|
+
<template #title>
|
|
87
|
+
<div :class="ns.e('drawer-title-wrap')">
|
|
88
|
+
<YhIcon name="document" />
|
|
89
|
+
<span>{{ t("ai.sources.drawerTitle") || "\u53C2\u8003\u6765\u6E90" }}</span>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|
|
92
|
+
<div :class="ns.e('drawer-content')">
|
|
93
|
+
<div
|
|
94
|
+
v-for="source in sources"
|
|
95
|
+
:key="source.id"
|
|
96
|
+
:class="[ns.e('source-card'), ns.is('active', activeSource?.id === source.id)]"
|
|
97
|
+
@click="handleClick(source)"
|
|
98
|
+
>
|
|
99
|
+
<div :class="ns.e('card-header')">
|
|
100
|
+
<div :class="ns.e('card-title-row')">
|
|
101
|
+
<YhIcon :name="getFileIcon(source.fileType)" :class="ns.e('file-icon')" />
|
|
102
|
+
<span :class="ns.e('card-title')">{{ source.title }}</span>
|
|
103
|
+
</div>
|
|
104
|
+
<div :class="ns.e('card-meta')">
|
|
105
|
+
<span v-if="source.source" :class="ns.e('source-name')">{{ source.source }}</span>
|
|
106
|
+
<span
|
|
107
|
+
v-if="showScore && source.score !== void 0"
|
|
108
|
+
:class="ns.e('score-badge')"
|
|
109
|
+
:style="{
|
|
111
110
|
color: scoreColor(source.score)
|
|
112
111
|
}"
|
|
113
|
-
>
|
|
114
|
-
{{ Math.round(source.score * 100) }}%
|
|
115
|
-
{{ t("ai.sources.relevant") || "\u76F8\u5173\u5EA6" }}
|
|
116
|
-
</span>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
<p v-if="source.excerpt" :class="ns.e('excerpt')">{{ source.excerpt }}</p>
|
|
120
|
-
<button
|
|
121
|
-
v-if="source.url"
|
|
122
|
-
:class="ns.e('open-btn')"
|
|
123
|
-
@click="handleOpen($event, source)"
|
|
124
112
|
>
|
|
125
|
-
|
|
126
|
-
{{ t("ai.sources.
|
|
127
|
-
</
|
|
113
|
+
{{ Math.round(source.score * 100) }}%
|
|
114
|
+
{{ t("ai.sources.relevant") || "\u76F8\u5173\u5EA6" }}
|
|
115
|
+
</span>
|
|
128
116
|
</div>
|
|
129
117
|
</div>
|
|
118
|
+
<p v-if="source.excerpt" :class="ns.e('excerpt')">{{ source.excerpt }}</p>
|
|
119
|
+
<button v-if="source.url" :class="ns.e('open-btn')" @click="handleOpen($event, source)">
|
|
120
|
+
<YhIcon name="arrow-right" />
|
|
121
|
+
{{ t("ai.sources.viewOriginal") || "\u67E5\u770B\u539F\u6587" }}
|
|
122
|
+
</button>
|
|
130
123
|
</div>
|
|
131
|
-
</
|
|
132
|
-
|
|
133
|
-
<!-- 遮罩 -->
|
|
134
|
-
<Transition name="yh-fade">
|
|
135
|
-
<div
|
|
136
|
-
v-if="drawerVisible"
|
|
137
|
-
:class="ns.e('drawer-overlay')"
|
|
138
|
-
@click="drawerVisible = false"
|
|
139
|
-
></div>
|
|
140
|
-
</Transition>
|
|
141
|
-
</Teleport>
|
|
124
|
+
</div>
|
|
125
|
+
</YhDrawer>
|
|
142
126
|
</template>
|
|
143
127
|
|
|
144
128
|
<!-- ── inline 模式:内联气泡列表 ── -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AiSourceItem } from './ai-sources';
|
|
2
|
-
declare var
|
|
2
|
+
declare var __VLS_65: {};
|
|
3
3
|
type __VLS_Slots = {} & {
|
|
4
|
-
default?: (props: typeof
|
|
4
|
+
default?: (props: typeof __VLS_65) => any;
|
|
5
5
|
};
|
|
6
6
|
declare const __VLS_component: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
7
7
|
readonly sources: {
|
|
@@ -22,10 +22,11 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
22
22
|
(0, _vitest.it)("should apply recording class when recording is true", () => {
|
|
23
23
|
const wrapper = (0, _testUtils.mount)(_aiVoiceTrigger.default, {
|
|
24
24
|
props: {
|
|
25
|
-
recording: true
|
|
25
|
+
recording: true,
|
|
26
|
+
teleport: false
|
|
26
27
|
}
|
|
27
28
|
});
|
|
28
|
-
(0, _vitest.expect)(wrapper.classes()).toContain("is-recording");
|
|
29
|
+
(0, _vitest.expect)(wrapper.find(".yh-ai-voice-trigger").classes()).toContain("is-recording");
|
|
29
30
|
(0, _vitest.expect)(wrapper.find(".yh-ai-voice-trigger__visualizer").exists()).toBe(true);
|
|
30
31
|
});
|
|
31
32
|
(0, _vitest.it)("should emit start and update:recording on click when initially false", async () => {
|
|
@@ -110,4 +111,20 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
110
111
|
});
|
|
111
112
|
(0, _vitest.expect)(wrapper.find(".yh-ai-voice-trigger__label").text()).toBe("Custom Trigger Text");
|
|
112
113
|
});
|
|
114
|
+
(0, _vitest.it)("should respect teleport prop", () => {
|
|
115
|
+
const wrapper = (0, _testUtils.mount)(_aiVoiceTrigger.default, {
|
|
116
|
+
props: {
|
|
117
|
+
variant: "sphere",
|
|
118
|
+
teleport: true
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
(0, _vitest.expect)(wrapper.props("teleport")).toBe(true);
|
|
122
|
+
const wrapperNoTeleport = (0, _testUtils.mount)(_aiVoiceTrigger.default, {
|
|
123
|
+
props: {
|
|
124
|
+
variant: "sphere",
|
|
125
|
+
teleport: false
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
(0, _vitest.expect)(wrapperNoTeleport.props("teleport")).toBe(false);
|
|
129
|
+
});
|
|
113
130
|
});
|