@mixd-id/web-scaffold 0.2.250801012 → 0.2.250801014
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/docs/components/AIDashboard.md +472 -0
- package/docs/components/WebPageBuilder.md +125 -0
- package/package.json +8 -30
- package/src/components/AIChat.vue +479 -0
- package/src/components/AIChatItem.vue +168 -0
- package/src/components/AIChatTextbox.vue +56 -0
- package/src/components/AIDashboard.vue +1113 -0
- package/src/components/AccountIcon.vue +19 -0
- package/src/components/Ahref.vue +200 -0
- package/src/components/Alert.vue +257 -0
- package/src/components/Article.vue +387 -0
- package/src/components/BarChart.vue +87 -0
- package/src/components/Block.vue +31 -0
- package/src/components/Button.vue +258 -0
- package/src/components/Card.vue +255 -0
- package/src/components/Carousel.vue +404 -0
- package/src/components/Cart.vue +192 -0
- package/src/components/CartIcon.vue +89 -0
- package/src/components/Chart.vue +486 -0
- package/src/components/ChatTyping.vue +69 -0
- package/src/components/Checkbox.vue +200 -0
- package/src/components/CodeEditor.vue +158 -0
- package/src/components/CollapsiblePanel.vue +70 -0
- package/src/components/ColorPicker.vue +653 -0
- package/src/components/Confirm.vue +180 -0
- package/src/components/ContextMenu.vue +265 -0
- package/src/components/ContextMenuItem.vue +53 -0
- package/src/components/CopyToClipboard.vue +34 -0
- package/src/components/Countdown.vue +264 -0
- package/src/components/Dashboard.vue +1151 -0
- package/src/components/Datepicker.vue +412 -0
- package/src/components/Datetimepicker.vue +355 -0
- package/src/components/DoughnutChart.vue +68 -0
- package/src/components/Dropdown.vue +314 -0
- package/src/components/FilterCard.vue +177 -0
- package/src/components/Flex.vue +44 -0
- package/src/components/Gmaps.vue +251 -0
- package/src/components/Grid.vue +35 -0
- package/src/components/HTMLEditor.vue +786 -0
- package/src/components/IconMenu2.vue +89 -0
- package/src/components/Image.vue +311 -0
- package/src/components/ImagePreview.vue +93 -0
- package/src/components/ImageUploader.vue +111 -0
- package/src/components/LineChart.vue +104 -0
- package/src/components/Link.vue +95 -0
- package/src/components/List.vue +1549 -0
- package/src/components/ListItem.vue +160 -0
- package/src/components/MarkdownEdit.vue +346 -0
- package/src/components/MarkdownPreview.vue +102 -0
- package/src/components/MenuItem1.vue +36 -0
- package/src/components/Metric.vue +105 -0
- package/src/components/MultiDropdown.vue +124 -0
- package/src/components/OTPField.vue +139 -0
- package/src/components/Paragraph.vue +40 -0
- package/src/components/PieChart.vue +67 -0
- package/src/components/PresetSelectorFilterItem.vue +204 -0
- package/src/components/Radio.vue +109 -0
- package/src/components/SearchModal.vue +153 -0
- package/src/components/Slider.vue +407 -0
- package/src/components/Svg.vue +33 -0
- package/src/components/SvgEditor.vue +173 -0
- package/src/components/Switch.vue +101 -0
- package/src/components/Table.vue +56 -0
- package/src/components/Tabs.vue +131 -0
- package/src/components/Testimonial.vue +86 -0
- package/src/components/Text.vue +34 -0
- package/src/components/TextBlock.vue +50 -0
- package/src/components/TextWithTag.vue +298 -0
- package/src/components/Textarea.vue +195 -0
- package/src/components/Textbox.vue +216 -0
- package/src/components/Thumbnail.vue +27 -0
- package/src/components/Timepicker.vue +223 -0
- package/src/components/Toast.vue +117 -0
- package/src/components/TreeMenu.vue +122 -0
- package/src/components/TreeView.vue +107 -0
- package/src/components/TreeViewItem.vue +452 -0
- package/src/components/Uploader.vue +45 -0
- package/src/components/Video.vue +119 -0
- package/src/components/VirtualGrid.vue +296 -0
- package/src/components/VirtualTable.vue +1047 -0
- package/src/components/YoutubeVideo.vue +196 -0
- package/src/configs/web-page-builder.js +118 -0
- package/src/directives/intersect.js +26 -0
- package/src/hooks/device.js +14 -0
- package/src/index.css +3 -0
- package/src/index.js +635 -0
- package/src/instructions/dashboard.js +203 -0
- package/src/mixin/component.js +306 -0
- package/src/themes/default/index.js +303 -0
- package/src/utils/ai-chat-answer.mjs +240 -0
- package/src/utils/ai-chat.js +167 -0
- package/src/utils/ai-dashboard-chat.mjs +371 -0
- package/src/utils/ai-dashboard.js +321 -0
- package/src/utils/ai-list-chat.mjs +407 -0
- package/src/utils/dashboard.js +317 -0
- package/src/utils/helpers.js +635 -0
- package/src/utils/helpers.mjs +722 -0
- package/src/utils/list.mjs +1564 -0
- package/src/utils/preset-selector.mjs +390 -0
- package/src/utils/selection.js +43 -0
- package/src/utils/wss.js +311 -0
- package/src/utils/wss.mjs +286 -0
- package/src/widgets/AhrefSetting.vue +97 -0
- package/src/widgets/ArticleSetting.vue +457 -0
- package/src/widgets/BackgroundColorSetting.vue +149 -0
- package/src/widgets/BlockSetting.vue +49 -0
- package/src/widgets/BorderColorSetting.vue +53 -0
- package/src/widgets/ButtonSetting.vue +113 -0
- package/src/widgets/CarouselSetting.vue +181 -0
- package/src/widgets/CartSetting.vue +46 -0
- package/src/widgets/CollapsiblePanelSetting.vue +46 -0
- package/src/widgets/ColumnSelector.vue +93 -0
- package/src/widgets/ComponentSetting.vue +2220 -0
- package/src/widgets/ComponentSetting2.vue +927 -0
- package/src/widgets/ContactForm.vue +193 -0
- package/src/widgets/ContactFormSetting.vue +395 -0
- package/src/widgets/CountdownSetting.vue +76 -0
- package/src/widgets/DataListSetting.vue +102 -0
- package/src/widgets/EmbeddedVideo.vue +60 -0
- package/src/widgets/EmbeddedVideoSetting.vue +50 -0
- package/src/widgets/FAQ.vue +98 -0
- package/src/widgets/FAQSetting.vue +94 -0
- package/src/widgets/FeatureList.vue +131 -0
- package/src/widgets/FeatureListSetting.vue +233 -0
- package/src/widgets/FlexSetting.vue +174 -0
- package/src/widgets/GridSetting.vue +165 -0
- package/src/widgets/Header2.vue +65 -0
- package/src/widgets/Header2Setting.vue +193 -0
- package/src/widgets/IconList.vue +51 -0
- package/src/widgets/IconListSetting.vue +188 -0
- package/src/widgets/ImageSetting.vue +119 -0
- package/src/widgets/LLMChat.vue +327 -0
- package/src/widgets/LinkSetting.vue +209 -0
- package/src/widgets/LinkSettingModal.vue +173 -0
- package/src/widgets/MenuItem1Setting.vue +78 -0
- package/src/widgets/ModalSetting.vue +94 -0
- package/src/widgets/MultiValueSetting.vue +121 -0
- package/src/widgets/MultiValueSetting2.vue +147 -0
- package/src/widgets/OGSettingModal.vue +103 -0
- package/src/widgets/ParagraphSetting.vue +89 -0
- package/src/widgets/PositionSetting.vue +209 -0
- package/src/widgets/PresetBar.vue +1011 -0
- package/src/widgets/PresetBarPivot.vue +330 -0
- package/src/widgets/PresetBarPivotColumnEdit.vue +186 -0
- package/src/widgets/PresetBarPivotValueEdit.vue +172 -0
- package/src/widgets/PresetSelector.vue +713 -0
- package/src/widgets/Review.vue +131 -0
- package/src/widgets/ReviewSetting.vue +201 -0
- package/src/widgets/SearchModalSetting.vue +70 -0
- package/src/widgets/Share.vue +164 -0
- package/src/widgets/ShareSetting.vue +99 -0
- package/src/widgets/SvgSetting.vue +133 -0
- package/src/widgets/TableSetting.vue +172 -0
- package/src/widgets/TestimonialSetting.vue +161 -0
- package/src/widgets/TextBlockSetting.vue +81 -0
- package/src/widgets/ThumbnailSetting.vue +49 -0
- package/src/widgets/WebComponentSelector.vue +205 -0
- package/src/widgets/WebDatasourceSelector.vue +153 -0
- package/src/widgets/WebLayoutSelector.vue +139 -0
- package/src/widgets/WebPageBuilder.vue +1962 -0
- package/src/widgets/YoutubeVideoSetting.vue +68 -0
- package/docs/components/ArrayList.md +0 -39
- package/docs/components/Box.md +0 -15
- package/docs/components/ChartBar.md +0 -34
- package/docs/components/DayTimeRange.md +0 -20
- package/docs/components/ErrorText.md +0 -15
- package/docs/components/FBLogin.md +0 -21
- package/docs/components/GHeatMaps.md +0 -16
- package/docs/components/GSignIn.md +0 -21
- package/docs/components/GmapsDirection.md +0 -20
- package/docs/components/IconMenu.md +0 -20
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="['flex w-full', message.direction === 1 ? 'justify-end' : 'justify-start']">
|
|
3
|
+
|
|
4
|
+
<!-- Typing indicator (assistant, no content yet) -->
|
|
5
|
+
<div v-if="message.isLoading && !message.display"
|
|
6
|
+
class="rounded-3xl px-5 py-4">
|
|
7
|
+
<div class="flex gap-1.5 items-center">
|
|
8
|
+
<span v-for="d in 3" :key="d" class="w-2 h-2 rounded-full bg-text-300 animate-bounce"
|
|
9
|
+
:style="`animation-delay:${(d - 1) * 160}ms`" />
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<!-- User bubble -->
|
|
14
|
+
<div v-else-if="message.direction === 1" class="max-w-[85%] flex flex-col items-end gap-2">
|
|
15
|
+
<!-- Image attachments -->
|
|
16
|
+
<div v-if="images.length" class="grid gap-2" :class="images.length > 1 ? 'grid-cols-2' : 'grid-cols-1'">
|
|
17
|
+
<img v-for="(img, n) in images" :key="n" :src="img" loading="lazy"
|
|
18
|
+
class="rounded-2xl max-h-60 w-full object-cover" />
|
|
19
|
+
</div>
|
|
20
|
+
<div v-if="message.body"
|
|
21
|
+
class="bg-base-500 rounded-3xl rounded-br-lg px-5 py-3 whitespace-pre-wrap md:text-lg leading-relaxed">
|
|
22
|
+
{{ message.body }}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<!-- Assistant bubble (markdown + streaming) -->
|
|
27
|
+
<div v-else class="max-w-full w-full">
|
|
28
|
+
<div :class="$style.md" class="md:text-lg leading-relaxed" v-html="renderedContent" />
|
|
29
|
+
<span v-if="message.isLoading" :class="$style.caret">▍</span>
|
|
30
|
+
|
|
31
|
+
<!-- Image attachments -->
|
|
32
|
+
<div v-if="images.length" class="mt-3 grid gap-2" :class="images.length > 1 ? 'grid-cols-2' : 'grid-cols-1 max-w-md'">
|
|
33
|
+
<img v-for="(img, n) in images" :key="n" :src="img" loading="lazy"
|
|
34
|
+
class="rounded-2xl w-full object-cover" />
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- File attachments -->
|
|
38
|
+
<div v-if="files.length" class="mt-3 flex flex-wrap gap-2">
|
|
39
|
+
<a v-for="(url, n) in files" :key="n" :href="url" target="_blank" rel="noopener"
|
|
40
|
+
class="flex items-center gap-2 p-2 pr-3 rounded-xl bg-base-300 hover:bg-base-500 transition-colors no-underline max-w-[260px]">
|
|
41
|
+
<span class="shrink-0 px-2 py-1 rounded-lg text-[10px] font-bold uppercase text-white" :style="{ background: fileColor(url) }">{{ fileExt(url) }}</span>
|
|
42
|
+
<span class="text-sm text-text truncate">{{ fileName(url) }}</span>
|
|
43
|
+
</a>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- Footer meta -->
|
|
47
|
+
<div v-if="!message.isLoading && (message.ellapsed || message.model)"
|
|
48
|
+
class="mt-2 flex items-center gap-1.5 text-xs text-text-300">
|
|
49
|
+
<span v-if="message.ellapsed">{{ message.ellapsed }}ms</span>
|
|
50
|
+
<span v-if="message.ellapsed && message.model">·</span>
|
|
51
|
+
<span v-if="message.model">{{ message.model }}</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<script setup>
|
|
59
|
+
import { computed } from 'vue'
|
|
60
|
+
import { Marked } from 'marked'
|
|
61
|
+
import hljs from 'highlight.js'
|
|
62
|
+
|
|
63
|
+
const { message } = defineProps({
|
|
64
|
+
message: { type: Object, required: true },
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// Local Marked instance — avoids mutating global marked state on each mount.
|
|
68
|
+
const markdown = new Marked({
|
|
69
|
+
gfm: true,
|
|
70
|
+
breaks: true,
|
|
71
|
+
renderer: {
|
|
72
|
+
code({ text, lang }) {
|
|
73
|
+
const language = lang && hljs.getLanguage(lang) ? lang : 'plaintext'
|
|
74
|
+
const html = hljs.highlight(text, { language }).value
|
|
75
|
+
return `<pre><code class="hljs language-${language}">${html}</code></pre>`
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const renderedContent = computed(() => markdown.parse(message.display ?? message.body ?? ''))
|
|
81
|
+
|
|
82
|
+
/* ---------- Attachments ---------- */
|
|
83
|
+
const attachments = computed(() => message.attachments ?? [])
|
|
84
|
+
const images = computed(() =>
|
|
85
|
+
attachments.value.filter(a => a.type === 'image').map(a => a.url ?? a.image),
|
|
86
|
+
)
|
|
87
|
+
const files = computed(() =>
|
|
88
|
+
attachments.value.filter(a => a.type === 'file' || a.type === 'url').map(a => a.url),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
function fileName(url) {
|
|
92
|
+
return (url || '').split('/').pop() || url
|
|
93
|
+
}
|
|
94
|
+
function fileExt(url) {
|
|
95
|
+
return ((url || '').split('.').pop() || 'file').slice(0, 4)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const EXT_COLORS = {
|
|
99
|
+
pdf: '#c0392b', doc: '#2b5fa8', docx: '#2b5fa8',
|
|
100
|
+
xls: '#1d6f42', xlsx: '#1d6f42', csv: '#1d6f42',
|
|
101
|
+
zip: '#7f8c8d', png: '#8e44ad', jpg: '#8e44ad',
|
|
102
|
+
}
|
|
103
|
+
function fileColor(url) {
|
|
104
|
+
return EXT_COLORS[fileExt(url).toLowerCase()] ?? '#444'
|
|
105
|
+
}
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<style module>
|
|
109
|
+
/* Animated caret shown while the assistant is streaming */
|
|
110
|
+
.caret {
|
|
111
|
+
@apply inline-block ml-0.5 text-text-300;
|
|
112
|
+
animation: blink 1s steps(2, start) infinite;
|
|
113
|
+
}
|
|
114
|
+
@keyframes blink {
|
|
115
|
+
to { visibility: hidden; }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Markdown layout. Explicit !important is required because tailwind.config.js
|
|
119
|
+
uses `important: true`, which makes preflight resets beat these rules. */
|
|
120
|
+
.md :global(p) { margin: 0.5em 0 !important; }
|
|
121
|
+
.md :global(p:first-child){ margin-top: 0 !important; }
|
|
122
|
+
.md :global(p:last-child) { margin-bottom: 0 !important; }
|
|
123
|
+
.md :global(ul) { list-style-type: disc !important; padding-left: 1.5em !important; margin: 0.6em 0 !important; }
|
|
124
|
+
.md :global(ol) { list-style-type: decimal !important; padding-left: 1.5em !important; margin: 0.6em 0 !important; }
|
|
125
|
+
.md :global(li) { display: list-item !important; margin: 0.2em 0 !important; }
|
|
126
|
+
.md :global(strong) { font-weight: 700 !important; }
|
|
127
|
+
.md :global(em) { font-style: italic !important; }
|
|
128
|
+
.md :global(a) { color: #89b4fa !important; text-decoration: underline !important; }
|
|
129
|
+
.md :global(h1), .md :global(h2),
|
|
130
|
+
.md :global(h3), .md :global(h4) { font-weight: 700 !important; margin: 0.8em 0 0.3em !important; line-height: 1.3 !important; }
|
|
131
|
+
.md :global(h1) { font-size: 1.5em !important; }
|
|
132
|
+
.md :global(h2) { font-size: 1.3em !important; }
|
|
133
|
+
.md :global(h3) { font-size: 1.1em !important; }
|
|
134
|
+
.md :global(table) { border-collapse: collapse !important; width: 100% !important; margin: 0.8em 0 !important; display: block; overflow-x: auto; }
|
|
135
|
+
.md :global(th), .md :global(td) { border: 1px solid rgba(128,128,128,0.35) !important; padding: 0.4em 0.7em !important; text-align: left !important; }
|
|
136
|
+
.md :global(th) { font-weight: 600 !important; background: rgba(128,128,128,0.1) !important; }
|
|
137
|
+
.md :global(blockquote) { border-left: 3px solid rgba(128,128,128,0.4) !important; padding-left: 1em !important; margin: 0.6em 0 !important; opacity: 0.8; }
|
|
138
|
+
.md :global(hr) { border-color: rgba(128,128,128,0.3) !important; margin: 1em 0 !important; }
|
|
139
|
+
.md :global(:not(pre) > code) {
|
|
140
|
+
background: rgba(128,128,128,0.18) !important;
|
|
141
|
+
padding: 0.12em 0.4em !important;
|
|
142
|
+
border-radius: 0.3em !important;
|
|
143
|
+
font-size: 0.88em !important;
|
|
144
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* highlight.js — Catppuccin Mocha. !important needed for the same reason. */
|
|
148
|
+
.md :global(code.hljs) {
|
|
149
|
+
display: block !important;
|
|
150
|
+
overflow-x: auto !important;
|
|
151
|
+
padding: 1rem !important;
|
|
152
|
+
border-radius: 0.5rem !important;
|
|
153
|
+
margin: 0.5rem 0 !important;
|
|
154
|
+
font-size: 0.85em !important;
|
|
155
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
|
|
156
|
+
background: #1e1e2e !important;
|
|
157
|
+
color: #cdd6f4 !important;
|
|
158
|
+
}
|
|
159
|
+
.md :global(code.hljs *) { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important; }
|
|
160
|
+
.md :global(.hljs-keyword), .md :global(.hljs-selector-tag), .md :global(.hljs-title) { color: #cba6f7 !important; }
|
|
161
|
+
.md :global(.hljs-string), .md :global(.hljs-attr) { color: #a6e3a1 !important; }
|
|
162
|
+
.md :global(.hljs-number), .md :global(.hljs-literal) { color: #fab387 !important; }
|
|
163
|
+
.md :global(.hljs-comment) { color: #6c7086 !important; font-style: italic; }
|
|
164
|
+
.md :global(.hljs-built_in), .md :global(.hljs-name) { color: #89b4fa !important; }
|
|
165
|
+
.md :global(.hljs-variable), .md :global(.hljs-template-variable) { color: #f38ba8 !important; }
|
|
166
|
+
.md :global(.hljs-type) { color: #f9e2af !important; }
|
|
167
|
+
.md :global(.hljs-tag) { color: #f38ba8 !important; }
|
|
168
|
+
</style>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-end gap-2 bg-base-500 rounded-3xl border-[1px] border-border-50 p-2 pl-4 focus-within:border-border-200 transition-colors">
|
|
3
|
+
<textarea ref="textarea"
|
|
4
|
+
v-model="draft"
|
|
5
|
+
rows="1"
|
|
6
|
+
class="resize-none flex-1 bg-transparent outline-none py-2 md:text-lg leading-relaxed max-h-[40vh]"
|
|
7
|
+
placeholder="Message AI…"
|
|
8
|
+
@input="resize"
|
|
9
|
+
@keydown="onKeydown" />
|
|
10
|
+
<button type="button"
|
|
11
|
+
class="shrink-0 w-9 h-9 rounded-full flex items-center justify-center transition-colors"
|
|
12
|
+
:class="canSend ? 'bg-primary hover:opacity-90' : 'bg-base-200 opacity-50 cursor-default'"
|
|
13
|
+
:disabled="!canSend"
|
|
14
|
+
@click="sending ? emit('cancel') : submit()">
|
|
15
|
+
<svg v-if="!sending" width="15" height="15" class="fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>
|
|
16
|
+
<svg v-else width="13" height="13" class="fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M0 128C0 92.7 28.7 64 64 64H320c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128z"/></svg>
|
|
17
|
+
</button>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script setup>
|
|
22
|
+
import { computed, nextTick, ref, useTemplateRef } from 'vue'
|
|
23
|
+
|
|
24
|
+
const { sending } = defineProps({
|
|
25
|
+
sending: { type: Boolean, default: false },
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const emit = defineEmits(['send', 'cancel'])
|
|
29
|
+
|
|
30
|
+
const draft = ref('')
|
|
31
|
+
const textarea = useTemplateRef('textarea')
|
|
32
|
+
|
|
33
|
+
const canSend = computed(() => sending || draft.value.trim().length > 0)
|
|
34
|
+
|
|
35
|
+
function resize() {
|
|
36
|
+
const el = textarea.value
|
|
37
|
+
if (!el) return
|
|
38
|
+
el.style.height = 'auto'
|
|
39
|
+
el.style.height = `${el.scrollHeight}px`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function onKeydown(e) {
|
|
43
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
44
|
+
e.preventDefault()
|
|
45
|
+
submit()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function submit() {
|
|
50
|
+
const content = draft.value.trim()
|
|
51
|
+
if (!content || sending) return
|
|
52
|
+
draft.value = ''
|
|
53
|
+
nextTick(resize)
|
|
54
|
+
emit('send', content)
|
|
55
|
+
}
|
|
56
|
+
</script>
|