@aleph-alpha/chat-kit 2.2.0 → 3.0.0
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/{CkInput.vue_vue_type_script_setup_true_lang-CjfKRuZs.js → CkInput.vue_vue_type_script_setup_true_lang-CwH5M5WD.js} +263 -142
- package/dist/chat-kit.css +33 -6
- package/dist/components/base/CkDropZoneOverlay/CkDropZoneOverlay.vue.d.ts.map +1 -1
- package/dist/components/base/CkFileItemsList/CkFileItemsList.stories.d.ts +7 -0
- package/dist/components/base/CkFileItemsList/CkFileItemsList.stories.d.ts.map +1 -1
- package/dist/components/base/CkFileItemsList/CkFileItemsList.vue.d.ts +1 -0
- package/dist/components/base/CkFileItemsList/CkFileItemsList.vue.d.ts.map +1 -1
- package/dist/components/base/CkFileItemsList/types.d.ts +9 -0
- package/dist/components/base/CkFileItemsList/types.d.ts.map +1 -1
- package/dist/components/base/CkModelSelector/types.d.ts +1 -1
- package/dist/components/base/CkPromptInput/CkPromptInput.stories.d.ts +48 -5
- package/dist/components/base/CkPromptInput/CkPromptInput.stories.d.ts.map +1 -1
- package/dist/components/base/CkPromptInput/CkPromptInput.vue.d.ts +7 -6
- package/dist/components/base/CkPromptInput/CkPromptInput.vue.d.ts.map +1 -1
- package/dist/components/base/CkPromptInput/types.d.ts +11 -12
- package/dist/components/base/CkPromptInput/types.d.ts.map +1 -1
- package/dist/components/base/CkPromptMenuButton/CkPromptMenuButton.stories.d.ts +18 -0
- package/dist/components/base/CkPromptMenuButton/CkPromptMenuButton.stories.d.ts.map +1 -0
- package/dist/components/base/CkPromptMenuButton/CkPromptMenuButton.vue.d.ts +18 -0
- package/dist/components/base/CkPromptMenuButton/CkPromptMenuButton.vue.d.ts.map +1 -0
- package/dist/components/base/CkPromptMenuButton/index.d.ts +3 -0
- package/dist/components/base/CkPromptMenuButton/index.d.ts.map +1 -0
- package/dist/components/base/CkPromptMenuButton/types.d.ts +30 -0
- package/dist/components/base/CkPromptMenuButton/types.d.ts.map +1 -0
- package/dist/components/base/{CkPromptActionButton/CkPromptActionButton.stories.d.ts → CkPromptSendButton/CkPromptSendButton.stories.d.ts} +5 -5
- package/dist/components/base/CkPromptSendButton/CkPromptSendButton.stories.d.ts.map +1 -0
- package/dist/components/base/{CkPromptActionButton/CkPromptActionButton.vue.d.ts → CkPromptSendButton/CkPromptSendButton.vue.d.ts} +3 -3
- package/dist/components/base/CkPromptSendButton/CkPromptSendButton.vue.d.ts.map +1 -0
- package/dist/components/base/CkPromptSendButton/index.d.ts +3 -0
- package/dist/components/base/CkPromptSendButton/index.d.ts.map +1 -0
- package/dist/components/base/CkPromptSendButton/types.d.ts +34 -0
- package/dist/components/base/CkPromptSendButton/types.d.ts.map +1 -0
- package/dist/components/base/CkPromptStopButton/CkPromptStopButton.stories.d.ts +1 -1
- package/dist/components/base/CkPromptStopButton/CkPromptStopButton.vue.d.ts.map +1 -1
- package/dist/components/base/CkPromptStopButton/types.d.ts +6 -4
- package/dist/components/base/CkPromptStopButton/types.d.ts.map +1 -1
- package/dist/components/base/icons/CkSendIcon.vue.d.ts.map +1 -1
- package/dist/components/base/index.d.ts +3 -1
- package/dist/components/base/index.d.ts.map +1 -1
- package/dist/components/composed/CkHistory/CkHistory.vue.d.ts +1 -1
- package/dist/components/composed/CkInput/CkInput.stories.d.ts.map +1 -1
- package/dist/components/composed/CkInput/CkInput.vue.d.ts +9 -8
- package/dist/components/composed/CkInput/CkInput.vue.d.ts.map +1 -1
- package/dist/components/composed/CkInput/types.d.ts +2 -7
- package/dist/components/composed/CkInput/types.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +9 -8
- package/dist/composables/fileUpload/types.d.ts +1 -1
- package/dist/composables/fileUpload/types.d.ts.map +1 -1
- package/dist/composables/fileUpload/useFileUpload.d.ts.map +1 -1
- package/dist/composables/index.js +1 -1
- package/dist/{index-CfXEGjc7.js → index-THzZwfMU.js} +6 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -10
- package/package.json +1 -1
- package/src/components/base/CkDropZoneOverlay/CkDropZoneOverlay.vue +2 -7
- package/src/components/base/CkFileItemsList/CkFileItemsList.spec.ts +67 -0
- package/src/components/base/CkFileItemsList/CkFileItemsList.stories.ts +48 -0
- package/src/components/base/CkFileItemsList/CkFileItemsList.vue +78 -4
- package/src/components/base/CkFileItemsList/types.ts +9 -0
- package/src/components/base/CkModelSelector/types.ts +1 -1
- package/src/components/base/CkPromptInput/CkPromptInput.spec.ts +206 -11
- package/src/components/base/CkPromptInput/CkPromptInput.stories.ts +1068 -43
- package/src/components/base/CkPromptInput/CkPromptInput.vue +137 -28
- package/src/components/base/CkPromptInput/types.ts +11 -12
- package/src/components/base/CkPromptMenuButton/CkPromptMenuButton.spec.ts +62 -0
- package/src/components/base/CkPromptMenuButton/CkPromptMenuButton.stories.ts +75 -0
- package/src/components/base/CkPromptMenuButton/CkPromptMenuButton.vue +26 -0
- package/src/components/base/CkPromptMenuButton/index.ts +2 -0
- package/src/components/base/CkPromptMenuButton/types.ts +29 -0
- package/src/components/base/{CkPromptActionButton/CkPromptActionButton.spec.ts → CkPromptSendButton/CkPromptSendButton.spec.ts} +11 -11
- package/src/components/base/CkPromptSendButton/CkPromptSendButton.stories.ts +117 -0
- package/src/components/base/{CkPromptActionButton/CkPromptActionButton.vue → CkPromptSendButton/CkPromptSendButton.vue} +4 -4
- package/src/components/base/CkPromptSendButton/index.ts +2 -0
- package/src/components/base/CkPromptSendButton/types.ts +33 -0
- package/src/components/base/CkPromptStopButton/CkPromptStopButton.spec.ts +5 -5
- package/src/components/base/CkPromptStopButton/CkPromptStopButton.stories.ts +1 -1
- package/src/components/base/CkPromptStopButton/CkPromptStopButton.vue +3 -2
- package/src/components/base/CkPromptStopButton/types.ts +6 -4
- package/src/components/base/icons/CkSendIcon.vue +3 -16
- package/src/components/base/icons/CkStopIcon.vue +1 -1
- package/src/components/base/index.ts +3 -1
- package/src/components/composed/CkInput/CkInput.stories.ts +0 -4
- package/src/components/composed/CkInput/CkInput.vue +7 -5
- package/src/components/composed/CkInput/types.ts +2 -7
- package/src/components/index.ts +3 -1
- package/src/composables/fileUpload/types.ts +1 -1
- package/src/composables/fileUpload/useFileUpload.spec.ts +26 -0
- package/src/composables/fileUpload/useFileUpload.ts +12 -3
- package/src/composables/fileUpload/useFileUploadWrapper.ts +1 -1
- package/src/index.ts +3 -1
- package/dist/components/base/CkPromptActionButton/CkPromptActionButton.stories.d.ts.map +0 -1
- package/dist/components/base/CkPromptActionButton/CkPromptActionButton.vue.d.ts.map +0 -1
- package/dist/components/base/CkPromptActionButton/index.d.ts +0 -3
- package/dist/components/base/CkPromptActionButton/index.d.ts.map +0 -1
- package/dist/components/base/CkPromptActionButton/types.d.ts +0 -33
- package/dist/components/base/CkPromptActionButton/types.d.ts.map +0 -1
- package/src/components/base/CkPromptActionButton/CkPromptActionButton.stories.ts +0 -126
- package/src/components/base/CkPromptActionButton/index.ts +0 -2
- package/src/components/base/CkPromptActionButton/types.ts +0 -32
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import CkPromptInput from './CkPromptInput.vue';
|
|
2
|
+
import CkPromptSendButton from '../CkPromptSendButton/CkPromptSendButton.vue';
|
|
3
|
+
import CkPromptStopButton from '../CkPromptStopButton/CkPromptStopButton.vue';
|
|
4
|
+
import CkPromptMenuButton from '../CkPromptMenuButton/CkPromptMenuButton.vue';
|
|
5
|
+
import CkPromptInputDisclaimer from '../CkPromptInputDisclaimer/CkPromptInputDisclaimer.vue';
|
|
6
|
+
import CkModelSelector from '../CkModelSelector/CkModelSelector.vue';
|
|
7
|
+
import CkFileItemsList from '../CkFileItemsList/CkFileItemsList.vue';
|
|
8
|
+
import CkDropZoneOverlay from '../CkDropZoneOverlay/CkDropZoneOverlay.vue';
|
|
9
|
+
import CkSendIcon from '../icons/CkSendIcon.vue';
|
|
10
|
+
import CkStopIcon from '../icons/CkStopIcon.vue';
|
|
11
|
+
import {
|
|
12
|
+
UiDropdownMenu,
|
|
13
|
+
UiDropdownMenuTrigger,
|
|
14
|
+
UiDropdownMenuContent,
|
|
15
|
+
UiDropdownMenuItem,
|
|
16
|
+
UiIcon,
|
|
17
|
+
formatFileSize,
|
|
18
|
+
} from '@aleph-alpha/ui-library';
|
|
19
|
+
import type { CkFileEntry } from '../CkFileItemsList/types';
|
|
20
|
+
import type { CkModelOption } from '../CkModelSelector/types';
|
|
21
|
+
import type { CkDropZoneRejection } from '../CkDropZoneOverlay/types';
|
|
2
22
|
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
3
|
-
import { ref } from 'vue';
|
|
23
|
+
import { computed, ref, useTemplateRef } from 'vue';
|
|
4
24
|
|
|
5
25
|
const meta: Meta<typeof CkPromptInput> = {
|
|
6
26
|
title: 'Base/CkPromptInput',
|
|
@@ -16,10 +36,6 @@ const meta: Meta<typeof CkPromptInput> = {
|
|
|
16
36
|
description: 'Placeholder shown when the textarea is empty.',
|
|
17
37
|
},
|
|
18
38
|
disabled: { control: 'boolean', description: 'Disables the textarea.' },
|
|
19
|
-
maxHeight: {
|
|
20
|
-
control: 'number',
|
|
21
|
-
description: 'Max height (px) before the textarea becomes scrollable.',
|
|
22
|
-
},
|
|
23
39
|
ariaLabel: {
|
|
24
40
|
control: 'text',
|
|
25
41
|
description: 'Accessible label for the textarea.',
|
|
@@ -37,7 +53,7 @@ export default meta;
|
|
|
37
53
|
type Story = StoryObj<typeof meta>;
|
|
38
54
|
|
|
39
55
|
const defaultTemplateSource = `<script setup lang="ts">
|
|
40
|
-
import { CkPromptInput } from '@aleph-alpha/chat-kit';
|
|
56
|
+
import { CkPromptInput, CkPromptSendButton, CkSendIcon } from '@aleph-alpha/chat-kit';
|
|
41
57
|
import { ref } from 'vue';
|
|
42
58
|
|
|
43
59
|
const text = ref('');
|
|
@@ -48,11 +64,33 @@ const text = ref('');
|
|
|
48
64
|
v-model="text"
|
|
49
65
|
placeholder="Type a message..."
|
|
50
66
|
aria-label="Message input"
|
|
51
|
-
|
|
67
|
+
>
|
|
68
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
69
|
+
<CkPromptSendButton
|
|
70
|
+
aria-label="Send message"
|
|
71
|
+
:disabled="disabled"
|
|
72
|
+
@click="submit"
|
|
73
|
+
>
|
|
74
|
+
<CkSendIcon />
|
|
75
|
+
</CkPromptSendButton>
|
|
76
|
+
</template>
|
|
77
|
+
</CkPromptInput>
|
|
52
78
|
</template>`;
|
|
53
79
|
|
|
54
80
|
const withToolbarSlotsTemplateSource = `<script setup lang="ts">
|
|
55
|
-
import {
|
|
81
|
+
import {
|
|
82
|
+
CkPromptInput,
|
|
83
|
+
CkPromptSendButton,
|
|
84
|
+
CkPromptMenuButton,
|
|
85
|
+
CkSendIcon,
|
|
86
|
+
} from '@aleph-alpha/chat-kit';
|
|
87
|
+
import {
|
|
88
|
+
UiDropdownMenu,
|
|
89
|
+
UiDropdownMenuTrigger,
|
|
90
|
+
UiDropdownMenuContent,
|
|
91
|
+
UiDropdownMenuItem,
|
|
92
|
+
UiIcon,
|
|
93
|
+
} from '@aleph-alpha/ui-library';
|
|
56
94
|
import { ref } from 'vue';
|
|
57
95
|
|
|
58
96
|
const text = ref('');
|
|
@@ -65,18 +103,41 @@ const text = ref('');
|
|
|
65
103
|
aria-label="Message input"
|
|
66
104
|
>
|
|
67
105
|
<template #toolbar-left>
|
|
68
|
-
<
|
|
106
|
+
<UiDropdownMenu>
|
|
107
|
+
<UiDropdownMenuTrigger as-child>
|
|
108
|
+
<CkPromptMenuButton aria-label="Open prompt menu" />
|
|
109
|
+
</UiDropdownMenuTrigger>
|
|
110
|
+
<UiDropdownMenuContent side="top" align="start" :side-offset="8">
|
|
111
|
+
<UiDropdownMenuItem>
|
|
112
|
+
<UiIcon name="file" :size="16" />
|
|
113
|
+
Add documents
|
|
114
|
+
</UiDropdownMenuItem>
|
|
115
|
+
<UiDropdownMenuItem>
|
|
116
|
+
<UiIcon name="folder" :size="16" />
|
|
117
|
+
Add folders
|
|
118
|
+
</UiDropdownMenuItem>
|
|
119
|
+
</UiDropdownMenuContent>
|
|
120
|
+
</UiDropdownMenu>
|
|
69
121
|
</template>
|
|
70
122
|
<template #toolbar-right="{ submit, disabled }">
|
|
71
|
-
<
|
|
72
|
-
Send
|
|
73
|
-
|
|
123
|
+
<CkPromptSendButton
|
|
124
|
+
aria-label="Send message"
|
|
125
|
+
:disabled="disabled"
|
|
126
|
+
@click="submit"
|
|
127
|
+
>
|
|
128
|
+
<CkSendIcon />
|
|
129
|
+
</CkPromptSendButton>
|
|
74
130
|
</template>
|
|
75
131
|
</CkPromptInput>
|
|
76
132
|
</template>`;
|
|
77
133
|
|
|
78
134
|
const withFooterSlotTemplateSource = `<script setup lang="ts">
|
|
79
|
-
import {
|
|
135
|
+
import {
|
|
136
|
+
CkPromptInput,
|
|
137
|
+
CkPromptSendButton,
|
|
138
|
+
CkPromptInputDisclaimer,
|
|
139
|
+
CkSendIcon,
|
|
140
|
+
} from '@aleph-alpha/chat-kit';
|
|
80
141
|
import { ref } from 'vue';
|
|
81
142
|
|
|
82
143
|
const text = ref('');
|
|
@@ -84,16 +145,101 @@ const text = ref('');
|
|
|
84
145
|
|
|
85
146
|
<template>
|
|
86
147
|
<CkPromptInput v-model="text" placeholder="Type a message..." aria-label="Message input">
|
|
148
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
149
|
+
<CkPromptSendButton
|
|
150
|
+
aria-label="Send message"
|
|
151
|
+
:disabled="disabled"
|
|
152
|
+
@click="submit"
|
|
153
|
+
>
|
|
154
|
+
<CkSendIcon />
|
|
155
|
+
</CkPromptSendButton>
|
|
156
|
+
</template>
|
|
87
157
|
<template #footer>
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
158
|
+
<CkPromptInputDisclaimer
|
|
159
|
+
text="AI can hallucinate confident-sounding but false information. Verify before you act on it."
|
|
160
|
+
/>
|
|
161
|
+
</template>
|
|
162
|
+
</CkPromptInput>
|
|
163
|
+
</template>`;
|
|
164
|
+
|
|
165
|
+
const withFooterControlsTemplateSource = `<script setup lang="ts">
|
|
166
|
+
import { CkPromptInput, CkPromptSendButton, CkSendIcon } from '@aleph-alpha/chat-kit';
|
|
167
|
+
import { ref, computed } from 'vue';
|
|
168
|
+
|
|
169
|
+
const text = ref('');
|
|
170
|
+
const maxChars = 500;
|
|
171
|
+
const charsLeft = computed(() => maxChars - text.value.length);
|
|
172
|
+
</script>
|
|
173
|
+
|
|
174
|
+
<template>
|
|
175
|
+
<CkPromptInput v-model="text" placeholder="Type a message..." aria-label="Message input">
|
|
176
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
177
|
+
<CkPromptSendButton
|
|
178
|
+
aria-label="Send message"
|
|
179
|
+
:disabled="disabled"
|
|
180
|
+
@click="submit"
|
|
181
|
+
>
|
|
182
|
+
<CkSendIcon />
|
|
183
|
+
</CkPromptSendButton>
|
|
184
|
+
</template>
|
|
185
|
+
<template #footer>
|
|
186
|
+
<div class="flex items-center justify-between text-xs text-content-on-surface-muted">
|
|
187
|
+
<span>
|
|
188
|
+
Press <kbd class="rounded border px-1 py-0.5">Shift</kbd>
|
|
189
|
+
+ <kbd class="rounded border px-1 py-0.5">Enter</kbd> for newline
|
|
190
|
+
</span>
|
|
191
|
+
<span :class="{ 'text-red-500': charsLeft < 0 }">
|
|
192
|
+
{{ charsLeft }} / {{ maxChars }}
|
|
193
|
+
</span>
|
|
194
|
+
</div>
|
|
195
|
+
</template>
|
|
196
|
+
</CkPromptInput>
|
|
197
|
+
</template>`;
|
|
198
|
+
|
|
199
|
+
const withToolbarTopSlotTemplateSource = `<script setup lang="ts">
|
|
200
|
+
import {
|
|
201
|
+
CkPromptInput,
|
|
202
|
+
CkPromptSendButton,
|
|
203
|
+
CkFileItemsList,
|
|
204
|
+
CkSendIcon,
|
|
205
|
+
type CkFileEntry,
|
|
206
|
+
} from '@aleph-alpha/chat-kit';
|
|
207
|
+
import { ref } from 'vue';
|
|
208
|
+
|
|
209
|
+
const text = ref('');
|
|
210
|
+
const files = ref<CkFileEntry[]>([
|
|
211
|
+
{ id: 'a', filename: 'Invoice.pdf', typeLabel: 'PDF', sizeLabel: '12 MB' },
|
|
212
|
+
{ id: 'b', filename: 'Data.csv', typeLabel: 'CSV', sizeLabel: '10 MB' },
|
|
213
|
+
]);
|
|
214
|
+
function removeFile(id: CkFileEntry['id']) {
|
|
215
|
+
files.value = files.value.filter((f) => f.id !== id);
|
|
216
|
+
}
|
|
217
|
+
</script>
|
|
218
|
+
|
|
219
|
+
<template>
|
|
220
|
+
<CkPromptInput v-model="text" placeholder="Type a message..." aria-label="Message input">
|
|
221
|
+
<template #toolbar-top>
|
|
222
|
+
<CkFileItemsList
|
|
223
|
+
:files="files"
|
|
224
|
+
is-removable
|
|
225
|
+
list-label="Attached files"
|
|
226
|
+
@remove="removeFile"
|
|
227
|
+
/>
|
|
228
|
+
</template>
|
|
229
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
230
|
+
<CkPromptSendButton
|
|
231
|
+
aria-label="Send message"
|
|
232
|
+
:disabled="disabled"
|
|
233
|
+
@click="submit"
|
|
234
|
+
>
|
|
235
|
+
<CkSendIcon />
|
|
236
|
+
</CkPromptSendButton>
|
|
91
237
|
</template>
|
|
92
238
|
</CkPromptInput>
|
|
93
239
|
</template>`;
|
|
94
240
|
|
|
95
241
|
const disabledTemplateSource = `<script setup lang="ts">
|
|
96
|
-
import { CkPromptInput } from '@aleph-alpha/chat-kit';
|
|
242
|
+
import { CkPromptInput, CkPromptSendButton, CkSendIcon } from '@aleph-alpha/chat-kit';
|
|
97
243
|
import { ref } from 'vue';
|
|
98
244
|
|
|
99
245
|
const text = ref('');
|
|
@@ -105,7 +251,17 @@ const text = ref('');
|
|
|
105
251
|
disabled
|
|
106
252
|
placeholder="Input is disabled"
|
|
107
253
|
aria-label="Disabled input"
|
|
108
|
-
|
|
254
|
+
>
|
|
255
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
256
|
+
<CkPromptSendButton
|
|
257
|
+
aria-label="Send message"
|
|
258
|
+
:disabled="disabled"
|
|
259
|
+
@click="submit"
|
|
260
|
+
>
|
|
261
|
+
<CkSendIcon />
|
|
262
|
+
</CkPromptSendButton>
|
|
263
|
+
</template>
|
|
264
|
+
</CkPromptInput>
|
|
109
265
|
</template>`;
|
|
110
266
|
|
|
111
267
|
/**
|
|
@@ -114,7 +270,7 @@ const text = ref('');
|
|
|
114
270
|
*/
|
|
115
271
|
export const Default: Story = {
|
|
116
272
|
render: () => ({
|
|
117
|
-
components: { CkPromptInput },
|
|
273
|
+
components: { CkPromptInput, CkPromptSendButton, CkSendIcon },
|
|
118
274
|
setup() {
|
|
119
275
|
const text = ref('');
|
|
120
276
|
return { text };
|
|
@@ -124,19 +280,41 @@ export const Default: Story = {
|
|
|
124
280
|
v-model="text"
|
|
125
281
|
placeholder="Type a message..."
|
|
126
282
|
aria-label="Message input"
|
|
127
|
-
|
|
283
|
+
>
|
|
284
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
285
|
+
<CkPromptSendButton
|
|
286
|
+
aria-label="Send message"
|
|
287
|
+
:disabled="disabled"
|
|
288
|
+
@click="submit"
|
|
289
|
+
>
|
|
290
|
+
<CkSendIcon />
|
|
291
|
+
</CkPromptSendButton>
|
|
292
|
+
</template>
|
|
293
|
+
</CkPromptInput>
|
|
128
294
|
`,
|
|
129
295
|
}),
|
|
130
296
|
parameters: { docs: { source: { code: defaultTemplateSource } } },
|
|
131
297
|
};
|
|
132
298
|
|
|
133
299
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
300
|
+
* Both toolbar slots filled with the real chat-kit primitives: the left slot
|
|
301
|
+
* hosts a `CkPromptMenuButton` wrapped in `UiDropdownMenu` (the canonical "+"
|
|
302
|
+
* trigger pattern with file/folder actions), the right slot a
|
|
303
|
+
* `CkPromptSendButton`. Mirrors Figma's "Drawer to add resources or tools".
|
|
136
304
|
*/
|
|
137
305
|
export const WithToolbarSlots: Story = {
|
|
138
306
|
render: () => ({
|
|
139
|
-
components: {
|
|
307
|
+
components: {
|
|
308
|
+
CkPromptInput,
|
|
309
|
+
CkPromptSendButton,
|
|
310
|
+
CkPromptMenuButton,
|
|
311
|
+
CkSendIcon,
|
|
312
|
+
UiDropdownMenu,
|
|
313
|
+
UiDropdownMenuTrigger,
|
|
314
|
+
UiDropdownMenuContent,
|
|
315
|
+
UiDropdownMenuItem,
|
|
316
|
+
UiIcon,
|
|
317
|
+
},
|
|
140
318
|
setup() {
|
|
141
319
|
const text = ref('');
|
|
142
320
|
return { text };
|
|
@@ -148,22 +326,30 @@ export const WithToolbarSlots: Story = {
|
|
|
148
326
|
aria-label="Message input"
|
|
149
327
|
>
|
|
150
328
|
<template #toolbar-left>
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
329
|
+
<UiDropdownMenu>
|
|
330
|
+
<UiDropdownMenuTrigger as-child>
|
|
331
|
+
<CkPromptMenuButton aria-label="Open prompt menu" />
|
|
332
|
+
</UiDropdownMenuTrigger>
|
|
333
|
+
<UiDropdownMenuContent side="top" align="start" :side-offset="8">
|
|
334
|
+
<UiDropdownMenuItem>
|
|
335
|
+
<UiIcon name="file" :size="16" />
|
|
336
|
+
Add documents
|
|
337
|
+
</UiDropdownMenuItem>
|
|
338
|
+
<UiDropdownMenuItem>
|
|
339
|
+
<UiIcon name="folder" :size="16" />
|
|
340
|
+
Add folders
|
|
341
|
+
</UiDropdownMenuItem>
|
|
342
|
+
</UiDropdownMenuContent>
|
|
343
|
+
</UiDropdownMenu>
|
|
157
344
|
</template>
|
|
158
345
|
<template #toolbar-right="{ submit, disabled }">
|
|
159
|
-
<
|
|
346
|
+
<CkPromptSendButton
|
|
347
|
+
aria-label="Send message"
|
|
160
348
|
:disabled="disabled"
|
|
161
|
-
class="flex items-center justify-center size-9 rounded-md disabled:opacity-50"
|
|
162
|
-
style="background-color: #c9e400; color: #1d1d1b;"
|
|
163
349
|
@click="submit"
|
|
164
350
|
>
|
|
165
|
-
<
|
|
166
|
-
</
|
|
351
|
+
<CkSendIcon />
|
|
352
|
+
</CkPromptSendButton>
|
|
167
353
|
</template>
|
|
168
354
|
</CkPromptInput>
|
|
169
355
|
`,
|
|
@@ -172,13 +358,19 @@ export const WithToolbarSlots: Story = {
|
|
|
172
358
|
};
|
|
173
359
|
|
|
174
360
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
361
|
+
* `footer` slot filled with the canonical `CkPromptInputDisclaimer` —
|
|
362
|
+
* the AI-hallucination warning matching the Figma design system. For
|
|
363
|
+
* arbitrary custom footer content (helpers, counters, etc.) see the
|
|
364
|
+
* `WithFooterControls` story below.
|
|
178
365
|
*/
|
|
179
366
|
export const WithFooterSlot: Story = {
|
|
180
367
|
render: () => ({
|
|
181
|
-
components: {
|
|
368
|
+
components: {
|
|
369
|
+
CkPromptInput,
|
|
370
|
+
CkPromptSendButton,
|
|
371
|
+
CkPromptInputDisclaimer,
|
|
372
|
+
CkSendIcon,
|
|
373
|
+
},
|
|
182
374
|
setup() {
|
|
183
375
|
const text = ref('');
|
|
184
376
|
return { text };
|
|
@@ -189,10 +381,19 @@ export const WithFooterSlot: Story = {
|
|
|
189
381
|
placeholder="Type a message..."
|
|
190
382
|
aria-label="Message input"
|
|
191
383
|
>
|
|
384
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
385
|
+
<CkPromptSendButton
|
|
386
|
+
aria-label="Send message"
|
|
387
|
+
:disabled="disabled"
|
|
388
|
+
@click="submit"
|
|
389
|
+
>
|
|
390
|
+
<CkSendIcon />
|
|
391
|
+
</CkPromptSendButton>
|
|
392
|
+
</template>
|
|
192
393
|
<template #footer>
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
|
|
394
|
+
<CkPromptInputDisclaimer
|
|
395
|
+
text="AI can hallucinate confident-sounding but false information. Verify before you act on it."
|
|
396
|
+
/>
|
|
196
397
|
</template>
|
|
197
398
|
</CkPromptInput>
|
|
198
399
|
`,
|
|
@@ -200,13 +401,827 @@ export const WithFooterSlot: Story = {
|
|
|
200
401
|
parameters: { docs: { source: { code: withFooterSlotTemplateSource } } },
|
|
201
402
|
};
|
|
202
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Footer slot used for interactive helpers — keyboard hint on the left and a
|
|
406
|
+
* live character counter on the right. Demonstrates that the footer can hold
|
|
407
|
+
* structured content, not just disclaimer text.
|
|
408
|
+
*/
|
|
409
|
+
export const WithFooterControls: Story = {
|
|
410
|
+
render: () => ({
|
|
411
|
+
components: { CkPromptInput, CkPromptSendButton, CkSendIcon },
|
|
412
|
+
setup() {
|
|
413
|
+
const text = ref('');
|
|
414
|
+
const maxChars = 500;
|
|
415
|
+
const charsLeft = computed(() => maxChars - text.value.length);
|
|
416
|
+
return { text, maxChars, charsLeft };
|
|
417
|
+
},
|
|
418
|
+
template: `
|
|
419
|
+
<CkPromptInput
|
|
420
|
+
v-model="text"
|
|
421
|
+
placeholder="Type a message..."
|
|
422
|
+
aria-label="Message input"
|
|
423
|
+
>
|
|
424
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
425
|
+
<CkPromptSendButton
|
|
426
|
+
aria-label="Send message"
|
|
427
|
+
:disabled="disabled"
|
|
428
|
+
@click="submit"
|
|
429
|
+
>
|
|
430
|
+
<CkSendIcon />
|
|
431
|
+
</CkPromptSendButton>
|
|
432
|
+
</template>
|
|
433
|
+
<template #footer>
|
|
434
|
+
<div class="text-content-on-surface-muted flex items-center justify-between text-xs">
|
|
435
|
+
<span>
|
|
436
|
+
Press
|
|
437
|
+
<kbd class="border-border-surface-default rounded border px-1 py-0.5 font-mono">Shift</kbd>
|
|
438
|
+
+
|
|
439
|
+
<kbd class="border-border-surface-default rounded border px-1 py-0.5 font-mono">Enter</kbd>
|
|
440
|
+
for newline
|
|
441
|
+
</span>
|
|
442
|
+
<span :class="{ 'text-red-500': charsLeft < 0 }">
|
|
443
|
+
{{ charsLeft }} / {{ maxChars }}
|
|
444
|
+
</span>
|
|
445
|
+
</div>
|
|
446
|
+
</template>
|
|
447
|
+
</CkPromptInput>
|
|
448
|
+
`,
|
|
449
|
+
}),
|
|
450
|
+
parameters: { docs: { source: { code: withFooterControlsTemplateSource } } },
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* `toolbar-top` slot rendered with the canonical `CkFileItemsList` primitive.
|
|
455
|
+
* Two attached files visible as inline chips. Adding the top row also flips
|
|
456
|
+
* the form to the multi-row layout (corner radius drops from 3xl to 2xl).
|
|
457
|
+
*/
|
|
458
|
+
export const WithToolbarTopSlot: Story = {
|
|
459
|
+
render: () => ({
|
|
460
|
+
components: {
|
|
461
|
+
CkPromptInput,
|
|
462
|
+
CkPromptSendButton,
|
|
463
|
+
CkFileItemsList,
|
|
464
|
+
CkSendIcon,
|
|
465
|
+
},
|
|
466
|
+
setup() {
|
|
467
|
+
const text = ref('');
|
|
468
|
+
const files = ref<CkFileEntry[]>([
|
|
469
|
+
{
|
|
470
|
+
id: 'a',
|
|
471
|
+
filename: 'Invoice.pdf',
|
|
472
|
+
typeLabel: 'PDF',
|
|
473
|
+
sizeLabel: '12 MB',
|
|
474
|
+
},
|
|
475
|
+
{ id: 'b', filename: 'Data.csv', typeLabel: 'CSV', sizeLabel: '10 MB' },
|
|
476
|
+
]);
|
|
477
|
+
function removeFile(id: CkFileEntry['id']) {
|
|
478
|
+
files.value = files.value.filter((f) => f.id !== id);
|
|
479
|
+
}
|
|
480
|
+
return { text, files, removeFile };
|
|
481
|
+
},
|
|
482
|
+
template: `
|
|
483
|
+
<CkPromptInput
|
|
484
|
+
v-model="text"
|
|
485
|
+
placeholder="Type a message..."
|
|
486
|
+
aria-label="Message input"
|
|
487
|
+
>
|
|
488
|
+
<template #toolbar-top>
|
|
489
|
+
<CkFileItemsList
|
|
490
|
+
:files="files"
|
|
491
|
+
is-removable
|
|
492
|
+
list-label="Attached files"
|
|
493
|
+
@remove="removeFile"
|
|
494
|
+
/>
|
|
495
|
+
</template>
|
|
496
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
497
|
+
<CkPromptSendButton
|
|
498
|
+
aria-label="Send message"
|
|
499
|
+
:disabled="disabled"
|
|
500
|
+
@click="submit"
|
|
501
|
+
>
|
|
502
|
+
<CkSendIcon />
|
|
503
|
+
</CkPromptSendButton>
|
|
504
|
+
</template>
|
|
505
|
+
</CkPromptInput>
|
|
506
|
+
`,
|
|
507
|
+
}),
|
|
508
|
+
parameters: { docs: { source: { code: withToolbarTopSlotTemplateSource } } },
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const withUploadAndModelSelectionTemplateSource = `<script setup lang="ts">
|
|
512
|
+
import {
|
|
513
|
+
CkPromptInput,
|
|
514
|
+
CkPromptSendButton,
|
|
515
|
+
CkPromptMenuButton,
|
|
516
|
+
CkPromptInputDisclaimer,
|
|
517
|
+
CkModelSelector,
|
|
518
|
+
CkFileItemsList,
|
|
519
|
+
CkDropZoneOverlay,
|
|
520
|
+
CkSendIcon,
|
|
521
|
+
type CkFileEntry,
|
|
522
|
+
type CkModelOption,
|
|
523
|
+
type CkDropZoneRejection,
|
|
524
|
+
} from '@aleph-alpha/chat-kit';
|
|
525
|
+
import {
|
|
526
|
+
UiDropdownMenu,
|
|
527
|
+
UiDropdownMenuTrigger,
|
|
528
|
+
UiDropdownMenuContent,
|
|
529
|
+
UiDropdownMenuItem,
|
|
530
|
+
UiIcon,
|
|
531
|
+
formatFileSize,
|
|
532
|
+
} from '@aleph-alpha/ui-library';
|
|
533
|
+
import { ref, useTemplateRef } from 'vue';
|
|
534
|
+
|
|
535
|
+
const text = ref('');
|
|
536
|
+
const files = ref<CkFileEntry[]>([]);
|
|
537
|
+
const selectedModelId = ref<string | null>('phoenix');
|
|
538
|
+
const fileInputRef = useTemplateRef<HTMLInputElement>('fileInputRef');
|
|
539
|
+
const folderInputRef = useTemplateRef<HTMLInputElement>('folderInputRef');
|
|
540
|
+
|
|
541
|
+
const models: CkModelOption[] = [
|
|
542
|
+
{ id: 'phoenix', label: 'Phoenix' },
|
|
543
|
+
{ id: 'pharia-1-llm-7b', label: 'Pharia 1 LLM 7B' },
|
|
544
|
+
{ id: 'pharia-1-llm-13b', label: 'Pharia 1 LLM 13B' },
|
|
545
|
+
];
|
|
546
|
+
|
|
547
|
+
function appendFiles(picked: File[]): void {
|
|
548
|
+
const next = picked.map((file) => ({
|
|
549
|
+
id: \`\${file.name}-\${file.size}-\${file.lastModified}\`,
|
|
550
|
+
filename: file.name,
|
|
551
|
+
typeLabel: file.name.split('.').pop()?.toUpperCase() ?? 'FILE',
|
|
552
|
+
sizeLabel: formatFileSize(file.size),
|
|
553
|
+
}));
|
|
554
|
+
files.value = [...files.value, ...next];
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function onFilePick(event: Event): void {
|
|
558
|
+
const input = event.target as HTMLInputElement;
|
|
559
|
+
appendFiles(Array.from(input.files ?? []));
|
|
560
|
+
input.value = '';
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function onRemoveFile(id: CkFileEntry['id']): void {
|
|
564
|
+
files.value = files.value.filter((file) => file.id !== id);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function onRejected(rejections: CkDropZoneRejection[]): void {
|
|
568
|
+
console.warn('Rejected files', rejections);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function onSubmit(value: string): void {
|
|
572
|
+
console.log('submit', { text: value, files: files.value, model: selectedModelId.value });
|
|
573
|
+
text.value = '';
|
|
574
|
+
files.value = [];
|
|
575
|
+
}
|
|
576
|
+
</script>
|
|
577
|
+
|
|
578
|
+
<template>
|
|
579
|
+
<CkDropZoneOverlay
|
|
580
|
+
accept=".pdf,.txt,.md,.csv,.docx"
|
|
581
|
+
:max-files="10"
|
|
582
|
+
:max-file-size-bytes="20 * 1024 * 1024"
|
|
583
|
+
@files="appendFiles"
|
|
584
|
+
@rejected="onRejected"
|
|
585
|
+
/>
|
|
586
|
+
<input
|
|
587
|
+
ref="fileInputRef"
|
|
588
|
+
type="file"
|
|
589
|
+
multiple
|
|
590
|
+
accept=".pdf,.txt,.md,.csv,.docx"
|
|
591
|
+
class="sr-only"
|
|
592
|
+
@change="onFilePick"
|
|
593
|
+
/>
|
|
594
|
+
<input
|
|
595
|
+
ref="folderInputRef"
|
|
596
|
+
type="file"
|
|
597
|
+
multiple
|
|
598
|
+
webkitdirectory
|
|
599
|
+
class="sr-only"
|
|
600
|
+
@change="onFilePick"
|
|
601
|
+
/>
|
|
602
|
+
<CkPromptInput
|
|
603
|
+
v-model="text"
|
|
604
|
+
placeholder="Ask me anything"
|
|
605
|
+
aria-label="Chat message input"
|
|
606
|
+
@submit="onSubmit"
|
|
607
|
+
>
|
|
608
|
+
<template v-if="files.length" #toolbar-top>
|
|
609
|
+
<CkFileItemsList
|
|
610
|
+
:files="files"
|
|
611
|
+
is-removable
|
|
612
|
+
list-label="Attached files"
|
|
613
|
+
@remove="onRemoveFile"
|
|
614
|
+
/>
|
|
615
|
+
</template>
|
|
616
|
+
<template #toolbar-left>
|
|
617
|
+
<UiDropdownMenu>
|
|
618
|
+
<UiDropdownMenuTrigger as-child>
|
|
619
|
+
<CkPromptMenuButton aria-label="Open prompt menu" />
|
|
620
|
+
</UiDropdownMenuTrigger>
|
|
621
|
+
<UiDropdownMenuContent side="top" align="start" :side-offset="8">
|
|
622
|
+
<UiDropdownMenuItem @select="fileInputRef?.click()">
|
|
623
|
+
<UiIcon name="file" :size="16" />
|
|
624
|
+
Add documents
|
|
625
|
+
</UiDropdownMenuItem>
|
|
626
|
+
<UiDropdownMenuItem @select="folderInputRef?.click()">
|
|
627
|
+
<UiIcon name="folder" :size="16" />
|
|
628
|
+
Add folders
|
|
629
|
+
</UiDropdownMenuItem>
|
|
630
|
+
</UiDropdownMenuContent>
|
|
631
|
+
</UiDropdownMenu>
|
|
632
|
+
</template>
|
|
633
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
634
|
+
<div class="flex items-center gap-3">
|
|
635
|
+
<CkModelSelector
|
|
636
|
+
:models="models"
|
|
637
|
+
v-model:selected-model-id="selectedModelId"
|
|
638
|
+
/>
|
|
639
|
+
<CkPromptSendButton
|
|
640
|
+
aria-label="Send message"
|
|
641
|
+
:disabled="disabled"
|
|
642
|
+
@click="submit"
|
|
643
|
+
>
|
|
644
|
+
<CkSendIcon />
|
|
645
|
+
</CkPromptSendButton>
|
|
646
|
+
</div>
|
|
647
|
+
</template>
|
|
648
|
+
<template #footer>
|
|
649
|
+
<CkPromptInputDisclaimer
|
|
650
|
+
text="AI can hallucinate confident-sounding but false information. Verify before you act on it."
|
|
651
|
+
/>
|
|
652
|
+
</template>
|
|
653
|
+
</CkPromptInput>
|
|
654
|
+
</template>`;
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* End-to-end composition matching the Figma "Prompt field" spec: file
|
|
658
|
+
* attachments via the `+` button and drag-and-drop, model selection next to
|
|
659
|
+
* the send button, and a hallucination disclaimer in the footer. File chips
|
|
660
|
+
* collapse to a popover once more than three are attached (`CkFileItemsList`
|
|
661
|
+
* default).
|
|
662
|
+
*/
|
|
663
|
+
export const WithUploadAndModelSelection: Story = {
|
|
664
|
+
render: () => ({
|
|
665
|
+
components: {
|
|
666
|
+
CkPromptInput,
|
|
667
|
+
CkPromptSendButton,
|
|
668
|
+
CkPromptMenuButton,
|
|
669
|
+
CkPromptInputDisclaimer,
|
|
670
|
+
CkModelSelector,
|
|
671
|
+
CkFileItemsList,
|
|
672
|
+
CkDropZoneOverlay,
|
|
673
|
+
CkSendIcon,
|
|
674
|
+
UiDropdownMenu,
|
|
675
|
+
UiDropdownMenuTrigger,
|
|
676
|
+
UiDropdownMenuContent,
|
|
677
|
+
UiDropdownMenuItem,
|
|
678
|
+
UiIcon,
|
|
679
|
+
},
|
|
680
|
+
setup() {
|
|
681
|
+
const text = ref('');
|
|
682
|
+
const files = ref<CkFileEntry[]>([]);
|
|
683
|
+
const selectedModelId = ref<string | null>('phoenix');
|
|
684
|
+
const fileInputRef = useTemplateRef<HTMLInputElement>('fileInputRef');
|
|
685
|
+
const folderInputRef = useTemplateRef<HTMLInputElement>('folderInputRef');
|
|
686
|
+
|
|
687
|
+
const models: CkModelOption[] = [
|
|
688
|
+
{ id: 'phoenix', label: 'Phoenix' },
|
|
689
|
+
{ id: 'pharia-1-llm-7b', label: 'Pharia 1 LLM 7B' },
|
|
690
|
+
{ id: 'pharia-1-llm-13b', label: 'Pharia 1 LLM 13B' },
|
|
691
|
+
];
|
|
692
|
+
|
|
693
|
+
function appendFiles(picked: File[]): void {
|
|
694
|
+
const next: CkFileEntry[] = picked.map((file) => ({
|
|
695
|
+
id: `${file.name}-${file.size}-${file.lastModified}`,
|
|
696
|
+
filename: file.name,
|
|
697
|
+
typeLabel: file.name.split('.').pop()?.toUpperCase() ?? 'FILE',
|
|
698
|
+
sizeLabel: formatFileSize(file.size),
|
|
699
|
+
}));
|
|
700
|
+
files.value = [...files.value, ...next];
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function onFilePick(event: Event): void {
|
|
704
|
+
const input = event.target as HTMLInputElement;
|
|
705
|
+
appendFiles(Array.from(input.files ?? []));
|
|
706
|
+
input.value = '';
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function onRemoveFile(id: CkFileEntry['id']): void {
|
|
710
|
+
files.value = files.value.filter((file) => file.id !== id);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function onRejected(rejections: CkDropZoneRejection[]): void {
|
|
714
|
+
// Surfaced by the built-in toast; consumer-side logging only.
|
|
715
|
+
// eslint-disable-next-line no-console
|
|
716
|
+
console.warn('Rejected files', rejections);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function onSubmit(value: string): void {
|
|
720
|
+
// eslint-disable-next-line no-console
|
|
721
|
+
console.log('submit', {
|
|
722
|
+
text: value,
|
|
723
|
+
files: files.value,
|
|
724
|
+
model: selectedModelId.value,
|
|
725
|
+
});
|
|
726
|
+
text.value = '';
|
|
727
|
+
files.value = [];
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return {
|
|
731
|
+
text,
|
|
732
|
+
files,
|
|
733
|
+
models,
|
|
734
|
+
selectedModelId,
|
|
735
|
+
fileInputRef,
|
|
736
|
+
folderInputRef,
|
|
737
|
+
appendFiles,
|
|
738
|
+
onFilePick,
|
|
739
|
+
onRemoveFile,
|
|
740
|
+
onRejected,
|
|
741
|
+
onSubmit,
|
|
742
|
+
};
|
|
743
|
+
},
|
|
744
|
+
template: `
|
|
745
|
+
<CkDropZoneOverlay
|
|
746
|
+
accept=".pdf,.txt,.md,.csv,.docx"
|
|
747
|
+
:max-files="10"
|
|
748
|
+
:max-file-size-bytes="20 * 1024 * 1024"
|
|
749
|
+
@files="appendFiles"
|
|
750
|
+
@rejected="onRejected"
|
|
751
|
+
/>
|
|
752
|
+
<input
|
|
753
|
+
ref="fileInputRef"
|
|
754
|
+
type="file"
|
|
755
|
+
multiple
|
|
756
|
+
accept=".pdf,.txt,.md,.csv,.docx"
|
|
757
|
+
class="sr-only"
|
|
758
|
+
@change="onFilePick"
|
|
759
|
+
/>
|
|
760
|
+
<input
|
|
761
|
+
ref="folderInputRef"
|
|
762
|
+
type="file"
|
|
763
|
+
multiple
|
|
764
|
+
webkitdirectory
|
|
765
|
+
class="sr-only"
|
|
766
|
+
@change="onFilePick"
|
|
767
|
+
/>
|
|
768
|
+
<CkPromptInput
|
|
769
|
+
v-model="text"
|
|
770
|
+
placeholder="Ask me anything"
|
|
771
|
+
aria-label="Chat message input"
|
|
772
|
+
@submit="onSubmit"
|
|
773
|
+
>
|
|
774
|
+
<template v-if="files.length" #toolbar-top>
|
|
775
|
+
<CkFileItemsList
|
|
776
|
+
:files="files"
|
|
777
|
+
is-removable
|
|
778
|
+
list-label="Attached files"
|
|
779
|
+
@remove="onRemoveFile"
|
|
780
|
+
/>
|
|
781
|
+
</template>
|
|
782
|
+
<template #toolbar-left>
|
|
783
|
+
<UiDropdownMenu>
|
|
784
|
+
<UiDropdownMenuTrigger as-child>
|
|
785
|
+
<CkPromptMenuButton aria-label="Open prompt menu" />
|
|
786
|
+
</UiDropdownMenuTrigger>
|
|
787
|
+
<UiDropdownMenuContent side="top" align="start" :side-offset="8">
|
|
788
|
+
<UiDropdownMenuItem @select="fileInputRef?.click()">
|
|
789
|
+
<UiIcon name="file" :size="16" />
|
|
790
|
+
Add documents
|
|
791
|
+
</UiDropdownMenuItem>
|
|
792
|
+
<UiDropdownMenuItem @select="folderInputRef?.click()">
|
|
793
|
+
<UiIcon name="folder" :size="16" />
|
|
794
|
+
Add folders
|
|
795
|
+
</UiDropdownMenuItem>
|
|
796
|
+
</UiDropdownMenuContent>
|
|
797
|
+
</UiDropdownMenu>
|
|
798
|
+
</template>
|
|
799
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
800
|
+
<div class="flex items-center gap-3">
|
|
801
|
+
<CkModelSelector
|
|
802
|
+
:models="models"
|
|
803
|
+
v-model:selected-model-id="selectedModelId"
|
|
804
|
+
/>
|
|
805
|
+
<CkPromptSendButton
|
|
806
|
+
aria-label="Send message"
|
|
807
|
+
:disabled="disabled"
|
|
808
|
+
@click="submit"
|
|
809
|
+
>
|
|
810
|
+
<CkSendIcon />
|
|
811
|
+
</CkPromptSendButton>
|
|
812
|
+
</div>
|
|
813
|
+
</template>
|
|
814
|
+
<template #footer>
|
|
815
|
+
<CkPromptInputDisclaimer
|
|
816
|
+
text="AI can hallucinate confident-sounding but false information. Verify before you act on it."
|
|
817
|
+
/>
|
|
818
|
+
</template>
|
|
819
|
+
</CkPromptInput>
|
|
820
|
+
`,
|
|
821
|
+
}),
|
|
822
|
+
parameters: {
|
|
823
|
+
docs: { source: { code: withUploadAndModelSelectionTemplateSource } },
|
|
824
|
+
},
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
const withMultiLineContentTemplateSource = `<script setup lang="ts">
|
|
828
|
+
import { CkPromptInput, CkPromptSendButton, CkSendIcon } from '@aleph-alpha/chat-kit';
|
|
829
|
+
import { ref } from 'vue';
|
|
830
|
+
|
|
831
|
+
const text = ref(
|
|
832
|
+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\\n' +
|
|
833
|
+
'Second line forces the layout to stack: textarea spans the full width,\\n' +
|
|
834
|
+
'toolbar drops to a dedicated row, form radius shrinks to 2xl.'
|
|
835
|
+
);
|
|
836
|
+
</script>
|
|
837
|
+
|
|
838
|
+
<template>
|
|
839
|
+
<CkPromptInput v-model="text" aria-label="Message input">
|
|
840
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
841
|
+
<CkPromptSendButton
|
|
842
|
+
aria-label="Send message"
|
|
843
|
+
:disabled="disabled"
|
|
844
|
+
@click="submit"
|
|
845
|
+
>
|
|
846
|
+
<CkSendIcon />
|
|
847
|
+
</CkPromptSendButton>
|
|
848
|
+
</template>
|
|
849
|
+
</CkPromptInput>
|
|
850
|
+
</template>`;
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Pre-filled with multi-line text so the multi-row layout is visible at
|
|
854
|
+
* mount: textarea spans the full width on its own row, toolbar drops below,
|
|
855
|
+
* and the form's corner radius shrinks from `rounded-3xl` to `rounded-2xl`.
|
|
856
|
+
*/
|
|
857
|
+
export const WithMultiLineContent: Story = {
|
|
858
|
+
render: () => ({
|
|
859
|
+
components: { CkPromptInput, CkPromptSendButton, CkSendIcon },
|
|
860
|
+
setup() {
|
|
861
|
+
const text = ref(
|
|
862
|
+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n' +
|
|
863
|
+
'Second line forces the layout to stack: textarea spans the full width,\n' +
|
|
864
|
+
'toolbar drops to a dedicated row, form radius shrinks to 2xl.',
|
|
865
|
+
);
|
|
866
|
+
return { text };
|
|
867
|
+
},
|
|
868
|
+
template: `
|
|
869
|
+
<CkPromptInput v-model="text" aria-label="Message input">
|
|
870
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
871
|
+
<CkPromptSendButton
|
|
872
|
+
aria-label="Send message"
|
|
873
|
+
:disabled="disabled"
|
|
874
|
+
@click="submit"
|
|
875
|
+
>
|
|
876
|
+
<CkSendIcon />
|
|
877
|
+
</CkPromptSendButton>
|
|
878
|
+
</template>
|
|
879
|
+
</CkPromptInput>
|
|
880
|
+
`,
|
|
881
|
+
}),
|
|
882
|
+
parameters: {
|
|
883
|
+
docs: { source: { code: withMultiLineContentTemplateSource } },
|
|
884
|
+
},
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
const withCollapsedFileChipsTemplateSource = `<script setup lang="ts">
|
|
888
|
+
import {
|
|
889
|
+
CkPromptInput,
|
|
890
|
+
CkPromptSendButton,
|
|
891
|
+
CkPromptMenuButton,
|
|
892
|
+
CkFileItemsList,
|
|
893
|
+
CkSendIcon,
|
|
894
|
+
type CkFileEntry,
|
|
895
|
+
} from '@aleph-alpha/chat-kit';
|
|
896
|
+
import {
|
|
897
|
+
UiDropdownMenu,
|
|
898
|
+
UiDropdownMenuTrigger,
|
|
899
|
+
UiDropdownMenuContent,
|
|
900
|
+
UiDropdownMenuItem,
|
|
901
|
+
UiIcon,
|
|
902
|
+
} from '@aleph-alpha/ui-library';
|
|
903
|
+
import { ref } from 'vue';
|
|
904
|
+
|
|
905
|
+
const text = ref('');
|
|
906
|
+
const files = ref<CkFileEntry[]>([
|
|
907
|
+
{ id: '1', filename: 'Invoice.pdf', typeLabel: 'PDF', sizeLabel: '12 MB' },
|
|
908
|
+
{ id: '2', filename: 'Data.csv', typeLabel: 'CSV', sizeLabel: '10 MB' },
|
|
909
|
+
{ id: '3', filename: 'Report.docx', typeLabel: 'DOCX', sizeLabel: '4 MB' },
|
|
910
|
+
{ id: '4', filename: 'Notes.md', typeLabel: 'MD', sizeLabel: '76 KB' },
|
|
911
|
+
{ id: '5', filename: 'schema.json', typeLabel: 'JSON', sizeLabel: '36 KB' },
|
|
912
|
+
]);
|
|
913
|
+
function removeFile(id: CkFileEntry['id']) {
|
|
914
|
+
files.value = files.value.filter((f) => f.id !== id);
|
|
915
|
+
}
|
|
916
|
+
</script>
|
|
917
|
+
|
|
918
|
+
<template>
|
|
919
|
+
<CkPromptInput v-model="text" aria-label="Message input">
|
|
920
|
+
<template #toolbar-top>
|
|
921
|
+
<CkFileItemsList
|
|
922
|
+
:files="files"
|
|
923
|
+
is-removable
|
|
924
|
+
list-label="Attached files"
|
|
925
|
+
@remove="removeFile"
|
|
926
|
+
/>
|
|
927
|
+
</template>
|
|
928
|
+
<template #toolbar-left>
|
|
929
|
+
<UiDropdownMenu>
|
|
930
|
+
<UiDropdownMenuTrigger as-child>
|
|
931
|
+
<CkPromptMenuButton aria-label="Open prompt menu" />
|
|
932
|
+
</UiDropdownMenuTrigger>
|
|
933
|
+
<UiDropdownMenuContent side="top" align="start" :side-offset="8">
|
|
934
|
+
<UiDropdownMenuItem>
|
|
935
|
+
<UiIcon name="file" :size="16" />
|
|
936
|
+
Add documents
|
|
937
|
+
</UiDropdownMenuItem>
|
|
938
|
+
<UiDropdownMenuItem>
|
|
939
|
+
<UiIcon name="folder" :size="16" />
|
|
940
|
+
Add folders
|
|
941
|
+
</UiDropdownMenuItem>
|
|
942
|
+
</UiDropdownMenuContent>
|
|
943
|
+
</UiDropdownMenu>
|
|
944
|
+
</template>
|
|
945
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
946
|
+
<CkPromptSendButton
|
|
947
|
+
aria-label="Send message"
|
|
948
|
+
:disabled="disabled"
|
|
949
|
+
@click="submit"
|
|
950
|
+
>
|
|
951
|
+
<CkSendIcon />
|
|
952
|
+
</CkPromptSendButton>
|
|
953
|
+
</template>
|
|
954
|
+
</CkPromptInput>
|
|
955
|
+
</template>`;
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Five attached files — past the default `maxInline=3` threshold of
|
|
959
|
+
* `CkFileItemsList`, so the list collapses to a single popover trigger
|
|
960
|
+
* labeled "5 files". Click it to inspect / remove the individual items.
|
|
961
|
+
* The toolbar-left `+` menu stays available so users can keep adding more.
|
|
962
|
+
*/
|
|
963
|
+
export const WithCollapsedFileChips: Story = {
|
|
964
|
+
render: () => ({
|
|
965
|
+
components: {
|
|
966
|
+
CkPromptInput,
|
|
967
|
+
CkPromptSendButton,
|
|
968
|
+
CkPromptMenuButton,
|
|
969
|
+
CkFileItemsList,
|
|
970
|
+
CkSendIcon,
|
|
971
|
+
UiDropdownMenu,
|
|
972
|
+
UiDropdownMenuTrigger,
|
|
973
|
+
UiDropdownMenuContent,
|
|
974
|
+
UiDropdownMenuItem,
|
|
975
|
+
UiIcon,
|
|
976
|
+
},
|
|
977
|
+
setup() {
|
|
978
|
+
const text = ref('');
|
|
979
|
+
const files = ref<CkFileEntry[]>([
|
|
980
|
+
{
|
|
981
|
+
id: '1',
|
|
982
|
+
filename: 'Invoice.pdf',
|
|
983
|
+
typeLabel: 'PDF',
|
|
984
|
+
sizeLabel: '12 MB',
|
|
985
|
+
},
|
|
986
|
+
{ id: '2', filename: 'Data.csv', typeLabel: 'CSV', sizeLabel: '10 MB' },
|
|
987
|
+
{
|
|
988
|
+
id: '3',
|
|
989
|
+
filename: 'Report.docx',
|
|
990
|
+
typeLabel: 'DOCX',
|
|
991
|
+
sizeLabel: '4 MB',
|
|
992
|
+
},
|
|
993
|
+
{ id: '4', filename: 'Notes.md', typeLabel: 'MD', sizeLabel: '76 KB' },
|
|
994
|
+
{
|
|
995
|
+
id: '5',
|
|
996
|
+
filename: 'schema.json',
|
|
997
|
+
typeLabel: 'JSON',
|
|
998
|
+
sizeLabel: '36 KB',
|
|
999
|
+
},
|
|
1000
|
+
]);
|
|
1001
|
+
function removeFile(id: CkFileEntry['id']) {
|
|
1002
|
+
files.value = files.value.filter((f) => f.id !== id);
|
|
1003
|
+
}
|
|
1004
|
+
return { text, files, removeFile };
|
|
1005
|
+
},
|
|
1006
|
+
template: `
|
|
1007
|
+
<CkPromptInput v-model="text" aria-label="Message input">
|
|
1008
|
+
<template #toolbar-top>
|
|
1009
|
+
<CkFileItemsList
|
|
1010
|
+
:files="files"
|
|
1011
|
+
is-removable
|
|
1012
|
+
list-label="Attached files"
|
|
1013
|
+
@remove="removeFile"
|
|
1014
|
+
/>
|
|
1015
|
+
</template>
|
|
1016
|
+
<template #toolbar-left>
|
|
1017
|
+
<UiDropdownMenu>
|
|
1018
|
+
<UiDropdownMenuTrigger as-child>
|
|
1019
|
+
<CkPromptMenuButton aria-label="Open prompt menu" />
|
|
1020
|
+
</UiDropdownMenuTrigger>
|
|
1021
|
+
<UiDropdownMenuContent side="top" align="start" :side-offset="8">
|
|
1022
|
+
<UiDropdownMenuItem>
|
|
1023
|
+
<UiIcon name="file" :size="16" />
|
|
1024
|
+
Add documents
|
|
1025
|
+
</UiDropdownMenuItem>
|
|
1026
|
+
<UiDropdownMenuItem>
|
|
1027
|
+
<UiIcon name="folder" :size="16" />
|
|
1028
|
+
Add folders
|
|
1029
|
+
</UiDropdownMenuItem>
|
|
1030
|
+
</UiDropdownMenuContent>
|
|
1031
|
+
</UiDropdownMenu>
|
|
1032
|
+
</template>
|
|
1033
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
1034
|
+
<CkPromptSendButton
|
|
1035
|
+
aria-label="Send message"
|
|
1036
|
+
:disabled="disabled"
|
|
1037
|
+
@click="submit"
|
|
1038
|
+
>
|
|
1039
|
+
<CkSendIcon />
|
|
1040
|
+
</CkPromptSendButton>
|
|
1041
|
+
</template>
|
|
1042
|
+
</CkPromptInput>
|
|
1043
|
+
`,
|
|
1044
|
+
}),
|
|
1045
|
+
parameters: {
|
|
1046
|
+
docs: { source: { code: withCollapsedFileChipsTemplateSource } },
|
|
1047
|
+
},
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
const withUploadAndModelSelectionStreamingTemplateSource = `<script setup lang="ts">
|
|
1051
|
+
import {
|
|
1052
|
+
CkPromptInput,
|
|
1053
|
+
CkPromptStopButton,
|
|
1054
|
+
CkPromptMenuButton,
|
|
1055
|
+
CkPromptInputDisclaimer,
|
|
1056
|
+
CkModelSelector,
|
|
1057
|
+
CkFileItemsList,
|
|
1058
|
+
CkStopIcon,
|
|
1059
|
+
type CkFileEntry,
|
|
1060
|
+
type CkModelOption,
|
|
1061
|
+
} from '@aleph-alpha/chat-kit';
|
|
1062
|
+
import {
|
|
1063
|
+
UiDropdownMenu,
|
|
1064
|
+
UiDropdownMenuTrigger,
|
|
1065
|
+
UiDropdownMenuContent,
|
|
1066
|
+
UiDropdownMenuItem,
|
|
1067
|
+
UiIcon,
|
|
1068
|
+
} from '@aleph-alpha/ui-library';
|
|
1069
|
+
import { ref } from 'vue';
|
|
1070
|
+
|
|
1071
|
+
const text = ref('Explain the difference between supervised and unsupervised learning.');
|
|
1072
|
+
const files = ref<CkFileEntry[]>([
|
|
1073
|
+
{ id: 'a', filename: 'Invoice.pdf', typeLabel: 'PDF', sizeLabel: '12 MB' },
|
|
1074
|
+
{ id: 'b', filename: 'Data.csv', typeLabel: 'CSV', sizeLabel: '10 MB' },
|
|
1075
|
+
]);
|
|
1076
|
+
const selectedModelId = ref<string | null>('phoenix');
|
|
1077
|
+
const models: CkModelOption[] = [
|
|
1078
|
+
{ id: 'phoenix', label: 'Phoenix' },
|
|
1079
|
+
{ id: 'pharia-1-llm-7b', label: 'Pharia 1 LLM 7B' },
|
|
1080
|
+
];
|
|
1081
|
+
function onStop() { /* abort streaming */ }
|
|
1082
|
+
</script>
|
|
1083
|
+
|
|
1084
|
+
<template>
|
|
1085
|
+
<CkPromptInput
|
|
1086
|
+
v-model="text"
|
|
1087
|
+
placeholder="Ask me anything"
|
|
1088
|
+
aria-label="Chat message input"
|
|
1089
|
+
disabled
|
|
1090
|
+
>
|
|
1091
|
+
<template #toolbar-top>
|
|
1092
|
+
<CkFileItemsList :files="files" list-label="Attached files" />
|
|
1093
|
+
</template>
|
|
1094
|
+
<template #toolbar-left>
|
|
1095
|
+
<UiDropdownMenu>
|
|
1096
|
+
<UiDropdownMenuTrigger as-child>
|
|
1097
|
+
<CkPromptMenuButton aria-label="Open prompt menu" disabled />
|
|
1098
|
+
</UiDropdownMenuTrigger>
|
|
1099
|
+
<UiDropdownMenuContent side="top" align="start" :side-offset="8">
|
|
1100
|
+
<UiDropdownMenuItem disabled>
|
|
1101
|
+
<UiIcon name="file" :size="16" />
|
|
1102
|
+
Add documents
|
|
1103
|
+
</UiDropdownMenuItem>
|
|
1104
|
+
</UiDropdownMenuContent>
|
|
1105
|
+
</UiDropdownMenu>
|
|
1106
|
+
</template>
|
|
1107
|
+
<template #toolbar-right>
|
|
1108
|
+
<div class="flex items-center gap-3">
|
|
1109
|
+
<CkModelSelector v-model:selected-model-id="selectedModelId" :models="models" />
|
|
1110
|
+
<CkPromptStopButton aria-label="Stop generating" @click="onStop">
|
|
1111
|
+
<CkStopIcon />
|
|
1112
|
+
</CkPromptStopButton>
|
|
1113
|
+
</div>
|
|
1114
|
+
</template>
|
|
1115
|
+
<template #footer>
|
|
1116
|
+
<CkPromptInputDisclaimer
|
|
1117
|
+
text="AI can hallucinate confident-sounding but false information. Verify before you act on it."
|
|
1118
|
+
/>
|
|
1119
|
+
</template>
|
|
1120
|
+
</CkPromptInput>
|
|
1121
|
+
</template>`;
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Streaming state mirroring the Figma "While Streaming" variant: the input
|
|
1125
|
+
* is `disabled`, the file chips and model selector remain visible (for
|
|
1126
|
+
* context), and `CkPromptStopButton` replaces `CkPromptSendButton` on the
|
|
1127
|
+
* right. Click the stop button to abort the in-flight assistant reply.
|
|
1128
|
+
*/
|
|
1129
|
+
export const WithUploadAndModelSelectionStreaming: Story = {
|
|
1130
|
+
render: () => ({
|
|
1131
|
+
components: {
|
|
1132
|
+
CkPromptInput,
|
|
1133
|
+
CkPromptStopButton,
|
|
1134
|
+
CkPromptMenuButton,
|
|
1135
|
+
CkPromptInputDisclaimer,
|
|
1136
|
+
CkModelSelector,
|
|
1137
|
+
CkFileItemsList,
|
|
1138
|
+
CkStopIcon,
|
|
1139
|
+
UiDropdownMenu,
|
|
1140
|
+
UiDropdownMenuTrigger,
|
|
1141
|
+
UiDropdownMenuContent,
|
|
1142
|
+
UiDropdownMenuItem,
|
|
1143
|
+
UiIcon,
|
|
1144
|
+
},
|
|
1145
|
+
setup() {
|
|
1146
|
+
const text = ref(
|
|
1147
|
+
'Explain the difference between supervised and unsupervised learning.',
|
|
1148
|
+
);
|
|
1149
|
+
const files = ref<CkFileEntry[]>([
|
|
1150
|
+
{
|
|
1151
|
+
id: 'a',
|
|
1152
|
+
filename: 'Invoice.pdf',
|
|
1153
|
+
typeLabel: 'PDF',
|
|
1154
|
+
sizeLabel: '12 MB',
|
|
1155
|
+
},
|
|
1156
|
+
{ id: 'b', filename: 'Data.csv', typeLabel: 'CSV', sizeLabel: '10 MB' },
|
|
1157
|
+
]);
|
|
1158
|
+
const selectedModelId = ref<string | null>('phoenix');
|
|
1159
|
+
const models: CkModelOption[] = [
|
|
1160
|
+
{ id: 'phoenix', label: 'Phoenix' },
|
|
1161
|
+
{ id: 'pharia-1-llm-7b', label: 'Pharia 1 LLM 7B' },
|
|
1162
|
+
];
|
|
1163
|
+
function onStop() {
|
|
1164
|
+
// eslint-disable-next-line no-console
|
|
1165
|
+
console.log('stop');
|
|
1166
|
+
}
|
|
1167
|
+
return { text, files, selectedModelId, models, onStop };
|
|
1168
|
+
},
|
|
1169
|
+
template: `
|
|
1170
|
+
<CkPromptInput
|
|
1171
|
+
v-model="text"
|
|
1172
|
+
placeholder="Ask me anything"
|
|
1173
|
+
aria-label="Chat message input"
|
|
1174
|
+
disabled
|
|
1175
|
+
>
|
|
1176
|
+
<template #toolbar-top>
|
|
1177
|
+
<CkFileItemsList :files="files" list-label="Attached files" />
|
|
1178
|
+
</template>
|
|
1179
|
+
<template #toolbar-left>
|
|
1180
|
+
<UiDropdownMenu>
|
|
1181
|
+
<UiDropdownMenuTrigger as-child>
|
|
1182
|
+
<CkPromptMenuButton aria-label="Open prompt menu" disabled />
|
|
1183
|
+
</UiDropdownMenuTrigger>
|
|
1184
|
+
<UiDropdownMenuContent side="top" align="start" :side-offset="8">
|
|
1185
|
+
<UiDropdownMenuItem disabled>
|
|
1186
|
+
<UiIcon name="file" :size="16" />
|
|
1187
|
+
Add documents
|
|
1188
|
+
</UiDropdownMenuItem>
|
|
1189
|
+
</UiDropdownMenuContent>
|
|
1190
|
+
</UiDropdownMenu>
|
|
1191
|
+
</template>
|
|
1192
|
+
<template #toolbar-right>
|
|
1193
|
+
<div class="flex items-center gap-3">
|
|
1194
|
+
<CkModelSelector
|
|
1195
|
+
v-model:selected-model-id="selectedModelId"
|
|
1196
|
+
:models="models"
|
|
1197
|
+
/>
|
|
1198
|
+
<CkPromptStopButton aria-label="Stop generating" @click="onStop">
|
|
1199
|
+
<CkStopIcon />
|
|
1200
|
+
</CkPromptStopButton>
|
|
1201
|
+
</div>
|
|
1202
|
+
</template>
|
|
1203
|
+
<template #footer>
|
|
1204
|
+
<CkPromptInputDisclaimer
|
|
1205
|
+
text="AI can hallucinate confident-sounding but false information. Verify before you act on it."
|
|
1206
|
+
/>
|
|
1207
|
+
</template>
|
|
1208
|
+
</CkPromptInput>
|
|
1209
|
+
`,
|
|
1210
|
+
}),
|
|
1211
|
+
parameters: {
|
|
1212
|
+
docs: {
|
|
1213
|
+
source: { code: withUploadAndModelSelectionStreamingTemplateSource },
|
|
1214
|
+
},
|
|
1215
|
+
},
|
|
1216
|
+
};
|
|
1217
|
+
|
|
203
1218
|
/**
|
|
204
1219
|
* Disabled state — the textarea cannot be edited and Enter does not submit.
|
|
205
1220
|
* Use while the server is processing the previous message.
|
|
206
1221
|
*/
|
|
207
1222
|
export const Disabled: Story = {
|
|
208
1223
|
render: () => ({
|
|
209
|
-
components: { CkPromptInput },
|
|
1224
|
+
components: { CkPromptInput, CkPromptSendButton, CkSendIcon },
|
|
210
1225
|
setup() {
|
|
211
1226
|
const text = ref('');
|
|
212
1227
|
return { text };
|
|
@@ -217,7 +1232,17 @@ export const Disabled: Story = {
|
|
|
217
1232
|
disabled
|
|
218
1233
|
placeholder="Input is disabled"
|
|
219
1234
|
aria-label="Disabled input"
|
|
220
|
-
|
|
1235
|
+
>
|
|
1236
|
+
<template #toolbar-right="{ submit, disabled }">
|
|
1237
|
+
<CkPromptSendButton
|
|
1238
|
+
aria-label="Send message"
|
|
1239
|
+
:disabled="disabled"
|
|
1240
|
+
@click="submit"
|
|
1241
|
+
>
|
|
1242
|
+
<CkSendIcon />
|
|
1243
|
+
</CkPromptSendButton>
|
|
1244
|
+
</template>
|
|
1245
|
+
</CkPromptInput>
|
|
221
1246
|
`,
|
|
222
1247
|
}),
|
|
223
1248
|
parameters: { docs: { source: { code: disabledTemplateSource } } },
|