@opentiny/tiny-robot 0.2.0-alpha.0 → 0.2.0-alpha.2

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.
Files changed (164) hide show
  1. package/dist/action-group/ActionGroup.vue.d.ts +26 -0
  2. package/dist/action-group/ActionGroupItem.vue.d.ts +18 -0
  3. package/dist/action-group/index.d.ts +12 -0
  4. package/dist/action-group/index.type.d.ts +17 -0
  5. package/dist/bubble/index.d.ts +2 -2
  6. package/dist/bubble/index.type.d.ts +16 -18
  7. package/dist/feedback/components/SourceList.vue.d.ts +11 -0
  8. package/dist/feedback/components/index.d.ts +1 -0
  9. package/dist/feedback/index.d.ts +7 -0
  10. package/dist/feedback/index.type.d.ts +25 -0
  11. package/dist/feedback/index.vue.d.ts +13 -0
  12. package/dist/history/components/index.d.ts +2 -0
  13. package/dist/history/components/item-tag.vue.d.ts +5 -0
  14. package/dist/history/components/search-empty.vue.d.ts +7 -0
  15. package/dist/history/composables/index.d.ts +1 -0
  16. package/dist/history/composables/useEditItemTitle.d.ts +12 -0
  17. package/dist/history/index.d.ts +6 -0
  18. package/dist/history/index.type.d.ts +43 -0
  19. package/dist/history/index.vue.d.ts +2 -0
  20. package/dist/icon-button/index.d.ts +7 -0
  21. package/dist/icon-button/index.type.d.ts +6 -0
  22. package/dist/icon-button/index.vue.d.ts +6 -0
  23. package/dist/index.d.ts +10 -1
  24. package/dist/index.js +57 -27
  25. package/dist/node_modules/.pnpm/@opentiny_utils@3.22.0/node_modules/@opentiny/utils/dist/index.es.js +1335 -884
  26. package/dist/node_modules/.pnpm/@opentiny_vue-common@3.22.0/node_modules/@opentiny/vue-common/lib/index.js +660 -144
  27. package/dist/node_modules/.pnpm/@opentiny_vue-hooks@3.22.0/node_modules/@opentiny/vue-hooks/dist/src/vue-popper.js +85 -0
  28. package/dist/node_modules/.pnpm/@opentiny_vue-locale@3.22.0/node_modules/@opentiny/vue-locale/lib/index.js +1783 -0
  29. package/dist/node_modules/.pnpm/@opentiny_vue-renderless@3.22.0/node_modules/@opentiny/vue-renderless/tooltip/index.js +77 -0
  30. package/dist/node_modules/.pnpm/@opentiny_vue-renderless@3.22.0/node_modules/@opentiny/vue-renderless/tooltip/vue.js +90 -0
  31. package/dist/node_modules/.pnpm/@opentiny_vue-tooltip@3.22.0/node_modules/@opentiny/vue-tooltip/lib/index.js +176 -0
  32. package/dist/node_modules/.pnpm/@opentiny_vue-tooltip@3.22.0/node_modules/@opentiny/vue-tooltip/lib/pc.js +248 -0
  33. package/dist/node_modules/.pnpm/@vueuse_core@13.1.0_vue@3.5.13/node_modules/@vueuse/core/index.js +310 -0
  34. package/dist/node_modules/.pnpm/@vueuse_shared@13.1.0_vue@3.5.13/node_modules/@vueuse/shared/index.js +110 -0
  35. package/dist/packages/components/src/action-group/ActionGroup.vue.js +7 -0
  36. package/dist/packages/components/src/action-group/ActionGroup.vue2.js +121 -0
  37. package/dist/packages/components/src/action-group/ActionGroupItem.vue.js +14 -0
  38. package/dist/packages/components/src/action-group/ActionGroupItem.vue2.js +4 -0
  39. package/dist/packages/components/src/action-group/index.js +17 -0
  40. package/dist/packages/components/src/bubble/Bubble.vue.js +7 -0
  41. package/dist/packages/components/src/bubble/Bubble.vue2.js +76 -0
  42. package/dist/packages/components/src/bubble/BubbleList.vue.js +7 -0
  43. package/dist/packages/components/src/bubble/BubbleList.vue2.js +50 -0
  44. package/dist/packages/components/src/bubble/index.js +2 -2
  45. package/dist/packages/components/src/container/index.vue.js +2 -2
  46. package/dist/packages/components/src/container/index.vue2.js +36 -36
  47. package/dist/packages/components/src/feedback/components/SourceList.vue.js +7 -0
  48. package/dist/packages/components/src/feedback/components/SourceList.vue2.js +52 -0
  49. package/dist/packages/components/src/feedback/index.js +9 -0
  50. package/dist/packages/components/src/feedback/index.vue.js +7 -0
  51. package/dist/packages/components/src/feedback/index.vue2.js +143 -0
  52. package/dist/packages/components/src/history/components/item-tag.vue.js +7 -0
  53. package/dist/packages/components/src/history/components/item-tag.vue2.js +21 -0
  54. package/dist/packages/components/src/history/components/search-empty.vue.js +7 -0
  55. package/dist/packages/components/src/history/components/search-empty.vue2.js +20 -0
  56. package/dist/packages/components/src/history/composables/useEditItemTitle.js +43 -0
  57. package/dist/packages/components/src/history/index.js +11 -0
  58. package/dist/packages/components/src/history/index.vue.js +7 -0
  59. package/dist/packages/components/src/history/index.vue2.js +130 -0
  60. package/dist/packages/components/src/icon-button/index.js +9 -0
  61. package/dist/packages/components/src/icon-button/index.vue.js +7 -0
  62. package/dist/packages/components/src/icon-button/index.vue2.js +22 -0
  63. package/dist/packages/components/src/prompts/prompt.vue.js +2 -2
  64. package/dist/packages/components/src/prompts/prompt.vue2.js +17 -15
  65. package/dist/packages/components/src/question/components/HotQuestions.vue.js +23 -23
  66. package/dist/packages/components/src/question/index.vue.js +18 -18
  67. package/dist/packages/components/src/sender/components/TemplateEditor.vue.js +7 -0
  68. package/dist/packages/components/src/sender/components/TemplateEditor.vue2.js +175 -0
  69. package/dist/packages/components/src/sender/index.vue.js +149 -128
  70. package/dist/packages/components/src/suggestion/components/CategoryNav.vue.js +38 -0
  71. package/dist/packages/components/src/suggestion/components/CategoryNav.vue2.js +4 -0
  72. package/dist/packages/components/src/suggestion/components/SuggestionCapsule.vue.js +107 -0
  73. package/dist/packages/components/src/suggestion/components/SuggestionCapsule.vue2.js +4 -0
  74. package/dist/packages/components/src/suggestion/components/SuggestionPanel.vue.js +123 -0
  75. package/dist/packages/components/src/suggestion/components/SuggestionPanel.vue2.js +4 -0
  76. package/dist/packages/components/src/suggestion/composables/useKeyboardNavigation.js +45 -0
  77. package/dist/packages/components/src/suggestion/composables/useTriggerDetection.js +17 -0
  78. package/dist/packages/components/src/suggestion/index.js +9 -0
  79. package/dist/packages/components/src/suggestion/index.vue.js +179 -0
  80. package/dist/packages/components/src/suggestion/index.vue2.js +4 -0
  81. package/dist/packages/components/src/suggestion/utils/dom.js +18 -0
  82. package/dist/packages/svgs/dist/tiny-robot-svgs.js +306 -90
  83. package/dist/question/components/HotQuestions.vue.d.ts +2 -2
  84. package/dist/sender/components/TemplateEditor.vue.d.ts +18 -0
  85. package/dist/sender/index.type.d.ts +47 -0
  86. package/dist/sender/index.vue.d.ts +70 -5
  87. package/dist/style.css +1 -1
  88. package/dist/suggestion/components/CategoryNav.vue.d.ts +45 -0
  89. package/dist/suggestion/components/SuggestionCapsule.vue.d.ts +32 -0
  90. package/dist/suggestion/components/SuggestionPanel.vue.d.ts +84 -0
  91. package/dist/suggestion/composables/useKeyboardNavigation.d.ts +18 -0
  92. package/dist/suggestion/composables/useSuggestionFilter.d.ts +10 -0
  93. package/dist/suggestion/composables/useTriggerDetection.d.ts +11 -0
  94. package/dist/suggestion/index.d.ts +7 -0
  95. package/dist/suggestion/index.type.d.ts +94 -0
  96. package/dist/suggestion/index.vue.d.ts +343 -0
  97. package/dist/suggestion/utils/dom.d.ts +20 -0
  98. package/package.json +4 -3
  99. package/src/action-group/ActionGroup.vue +247 -0
  100. package/src/action-group/ActionGroupItem.vue +9 -0
  101. package/src/action-group/index.ts +25 -0
  102. package/src/action-group/index.type.ts +21 -0
  103. package/src/bubble/Bubble.vue +153 -0
  104. package/src/bubble/BubbleList.vue +55 -0
  105. package/src/bubble/index.ts +2 -2
  106. package/src/bubble/index.type.ts +7 -21
  107. package/src/container/index.vue +10 -36
  108. package/src/feedback/components/SourceList.vue +112 -0
  109. package/src/feedback/components/index.ts +1 -0
  110. package/src/feedback/index.ts +12 -0
  111. package/src/feedback/index.type.ts +27 -0
  112. package/src/feedback/index.vue +166 -0
  113. package/src/history/components/index.ts +2 -0
  114. package/src/history/components/item-tag.vue +49 -0
  115. package/src/history/components/search-empty.vue +38 -0
  116. package/src/history/composables/index.ts +1 -0
  117. package/src/history/composables/useEditItemTitle.ts +75 -0
  118. package/src/history/index.ts +12 -0
  119. package/src/history/index.type.ts +50 -0
  120. package/src/history/index.vue +292 -0
  121. package/src/icon-button/index.ts +12 -0
  122. package/src/icon-button/index.type.ts +7 -0
  123. package/src/icon-button/index.vue +58 -0
  124. package/src/index.ts +33 -1
  125. package/src/prompts/prompt.vue +7 -21
  126. package/src/question/components/HotQuestions.vue +1 -1
  127. package/src/question/index.less +9 -10
  128. package/src/sender/components/TemplateEditor.vue +465 -0
  129. package/src/sender/index.less +17 -7
  130. package/src/sender/index.type.ts +51 -0
  131. package/src/sender/index.vue +56 -8
  132. package/src/sender/vars.less +3 -3
  133. package/src/suggestion/components/CategoryNav.vue +38 -0
  134. package/src/suggestion/components/SuggestionCapsule.vue +183 -0
  135. package/src/suggestion/components/SuggestionPanel.vue +147 -0
  136. package/src/suggestion/composables/useKeyboardNavigation.ts +101 -0
  137. package/src/suggestion/composables/useSuggestionFilter.ts +34 -0
  138. package/src/suggestion/composables/useTriggerDetection.ts +46 -0
  139. package/src/suggestion/index.less +497 -0
  140. package/src/suggestion/index.ts +12 -0
  141. package/src/suggestion/index.type.ts +101 -0
  142. package/src/suggestion/index.vue +338 -0
  143. package/src/suggestion/utils/dom.ts +66 -0
  144. package/src/suggestion/vars.less +141 -0
  145. package/dist/bubble/components/actions/copy.vue.d.ts +0 -10
  146. package/dist/bubble/components/actions/index.d.ts +0 -2
  147. package/dist/bubble/components/actions/refresh.vue.d.ts +0 -2
  148. package/dist/bubble/useScroll.d.ts +0 -4
  149. package/dist/packages/components/src/bubble/bubble-list.vue.js +0 -7
  150. package/dist/packages/components/src/bubble/bubble-list.vue2.js +0 -37
  151. package/dist/packages/components/src/bubble/bubble.vue.js +0 -7
  152. package/dist/packages/components/src/bubble/bubble.vue2.js +0 -118
  153. package/dist/packages/components/src/bubble/components/actions/copy.vue.js +0 -7
  154. package/dist/packages/components/src/bubble/components/actions/copy.vue2.js +0 -35
  155. package/dist/packages/components/src/bubble/components/actions/refresh.vue.js +0 -7
  156. package/dist/packages/components/src/bubble/components/actions/refresh.vue2.js +0 -16
  157. package/dist/packages/components/src/bubble/useScroll.js +0 -13
  158. package/src/bubble/bubble-list.vue +0 -42
  159. package/src/bubble/bubble.vue +0 -247
  160. package/src/bubble/components/actions/copy.vue +0 -54
  161. package/src/bubble/components/actions/index.ts +0 -2
  162. package/src/bubble/components/actions/refresh.vue +0 -31
  163. package/src/bubble/useScroll.ts +0 -14
  164. /package/dist/bubble/{bubble-list.vue.d.ts → BubbleList.vue.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentiny/tiny-robot",
3
- "version": "0.2.0-alpha.0",
3
+ "version": "0.2.0-alpha.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -16,12 +16,13 @@
16
16
  "vue": "^3.3.11"
17
17
  },
18
18
  "dependencies": {
19
- "@opentiny/tiny-robot-svgs": "0.2.0-alpha.0",
19
+ "@opentiny/tiny-robot-svgs": "0.2.0-alpha.2",
20
20
  "@opentiny/vue": "^3.21.0",
21
21
  "@opentiny/vue-button": "^3.21.0",
22
22
  "@opentiny/vue-icon": "^3.22.0",
23
23
  "@opentiny/vue-input": "^3.21.0",
24
24
  "@opentiny/vue-tooltip": "^3.22.0",
25
+ "@vueuse/core": "^13.1.0",
25
26
  "markdown-it": "^14.1.0"
26
27
  },
27
28
  "devDependencies": {
@@ -39,5 +40,5 @@
39
40
  "vue": "^3.3.11",
40
41
  "vue-tsc": "^2.2.8"
41
42
  },
42
- "gitHead": "3517724651afbbb05b1be20487a9f3341c06133c"
43
+ "gitHead": "f334d2ba1772fe0e2859d5f859980257e70f36e1"
43
44
  }
@@ -0,0 +1,247 @@
1
+ <script setup lang="ts">
2
+ import { IconMenu } from '@opentiny/tiny-robot-svgs'
3
+ import TinyTooltip from '@opentiny/vue-tooltip'
4
+ import { onClickOutside, useWindowSize } from '@vueuse/core'
5
+ import { computed, nextTick, ref, useTemplateRef, VNode, watch } from 'vue'
6
+ import IconButton from '../icon-button'
7
+ import { ActionGroupEvents, ActionGroupProps, ActionGroupSlots } from './index.type'
8
+
9
+ const props = defineProps<ActionGroupProps>()
10
+
11
+ const slots = defineSlots<ActionGroupSlots>()
12
+
13
+ const emit = defineEmits<ActionGroupEvents>()
14
+
15
+ const children = computed(() => {
16
+ const defaultSlot = slots.default()
17
+
18
+ if (Array.isArray(defaultSlot)) {
19
+ if (defaultSlot.length === 1 && defaultSlot[0].type?.toString() === 'Symbol(v-fgt)') {
20
+ return defaultSlot[0].children as VNode[]
21
+ }
22
+
23
+ return defaultSlot
24
+ }
25
+
26
+ if (defaultSlot.type?.toString() === 'Symbol(v-fgt)') {
27
+ return defaultSlot.children as VNode[]
28
+ }
29
+
30
+ return [defaultSlot]
31
+ })
32
+
33
+ const maxNum = computed(() => {
34
+ const n = props.maxNum ?? Number.MAX_SAFE_INTEGER
35
+
36
+ return n > 0 ? n : Number.MAX_SAFE_INTEGER
37
+ })
38
+
39
+ const showMore = computed(() => {
40
+ return children.value.length > maxNum.value
41
+ })
42
+
43
+ const list = computed(() => {
44
+ if (showMore.value) {
45
+ return children.value.slice(0, maxNum.value)
46
+ }
47
+
48
+ return children.value
49
+ })
50
+
51
+ const moreList = computed(() => {
52
+ if (showMore.value) {
53
+ return children.value.slice(maxNum.value)
54
+ }
55
+
56
+ return []
57
+ })
58
+
59
+ const moreBtn = useTemplateRef('moreBtnRef')
60
+ const dropDown = useTemplateRef('dropDownRef')
61
+ const showDropdown = ref(false)
62
+
63
+ const handleMoreClick = () => {
64
+ showDropdown.value = !showDropdown.value
65
+ }
66
+
67
+ onClickOutside(dropDown, (ev) => {
68
+ if (moreBtn.value?.contains(ev.target as Node)) {
69
+ return
70
+ }
71
+
72
+ showDropdown.value = false
73
+ })
74
+
75
+ const handleItemClick = (name: string) => {
76
+ emit('item-click', name)
77
+ showDropdown.value = false
78
+ }
79
+
80
+ const dropDownPlacement = ref('placement-bottom')
81
+ const { height: windowHeight } = useWindowSize()
82
+
83
+ const updateDropDownPlacement = () => {
84
+ if (!dropDown.value || !moreBtn.value) {
85
+ return 'placement-bottom'
86
+ }
87
+
88
+ const dropDownRect = dropDown.value.getBoundingClientRect()
89
+ const moreBtnRect = moreBtn.value.getBoundingClientRect()
90
+
91
+ dropDownPlacement.value =
92
+ moreBtnRect.bottom + dropDownRect.height + 16 > windowHeight.value ? 'placement-top' : 'placement-bottom'
93
+ }
94
+
95
+ watch(showDropdown, (show) => {
96
+ if (show) {
97
+ nextTick(() => {
98
+ updateDropDownPlacement()
99
+ })
100
+ }
101
+ })
102
+
103
+ watch(windowHeight, () => {
104
+ if (showDropdown.value) {
105
+ updateDropDownPlacement()
106
+ }
107
+ })
108
+ </script>
109
+
110
+ <template>
111
+ <div class="tr-action-group">
112
+ <tiny-tooltip
113
+ v-for="(item, index) in list"
114
+ :key="index"
115
+ :content="item.props?.label"
116
+ effect="dark"
117
+ placement="top"
118
+ :open-delay="500"
119
+ :disabled="!props.showTooltip"
120
+ >
121
+ <span class="tr-action-group__btn-wrapper" @click="handleItemClick(item.props?.name)">
122
+ <component :is="item" />
123
+ </span>
124
+ </tiny-tooltip>
125
+ <tiny-tooltip
126
+ v-if="showMore"
127
+ content="更多"
128
+ effect="dark"
129
+ placement="top"
130
+ :open-delay="500"
131
+ :disabled="!props.showTooltip"
132
+ >
133
+ <span ref="moreBtnRef" class="tr-action-group__btn-wrapper" @click="handleMoreClick">
134
+ <slot name="moreBtn">
135
+ <icon-button :icon="IconMenu" />
136
+ </slot>
137
+ <transition name="tr-action-group-dropdown">
138
+ <ul v-show="showDropdown" ref="dropDownRef" :class="['tr-action-group__dropdown', dropDownPlacement]">
139
+ <li
140
+ class="tr-action-group__dropdown-item"
141
+ v-for="(item, index) in moreList"
142
+ :key="index"
143
+ @click.stop="handleItemClick(item.props?.name)"
144
+ >
145
+ <component v-if="!props.dropDownShowLabelOnly" :is="item" />
146
+ <span class="tr-action-group__dropdown-item-text">{{ item.props?.label }}</span>
147
+ </li>
148
+ </ul>
149
+ </transition>
150
+ </span>
151
+ </tiny-tooltip>
152
+ </div>
153
+ </template>
154
+
155
+ <style lang="less" scoped>
156
+ .tr-action-group {
157
+ display: inline-flex;
158
+ align-items: center;
159
+ gap: 4px;
160
+
161
+ .tr-action-group__btn-wrapper {
162
+ display: inline-flex;
163
+ line-height: 0;
164
+ position: relative;
165
+ }
166
+
167
+ .tr-action-group__dropdown {
168
+ width: max-content;
169
+ position: absolute;
170
+ z-index: 100;
171
+ right: 0;
172
+ background-color: white;
173
+ padding: 4px;
174
+ border-radius: 12px;
175
+ box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
176
+
177
+ &.placement-top {
178
+ bottom: calc(100% + 8px);
179
+ }
180
+
181
+ &.placement-bottom {
182
+ top: calc(100% + 8px);
183
+ }
184
+
185
+ .tr-action-group__dropdown-item {
186
+ border-radius: 8px;
187
+ cursor: pointer;
188
+ height: 32px;
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: flex-start;
192
+ padding: 4px 16px 4px 8px;
193
+ gap: 4px;
194
+ transition: background-color 0.3s;
195
+
196
+ &:not(:first-child) {
197
+ margin-top: 4px;
198
+ }
199
+
200
+ &:hover {
201
+ background-color: rgba(0, 0, 0, 0.04);
202
+ }
203
+
204
+ &:active {
205
+ background-color: rgba(0, 0, 0, 0.15);
206
+ }
207
+
208
+ & > * {
209
+ flex-shrink: 0;
210
+ }
211
+
212
+ &:has(> *:only-child) {
213
+ padding: 4px 16px;
214
+ }
215
+
216
+ .tr-action-group__dropdown-item-text {
217
+ font-size: 12px;
218
+ line-height: 20px;
219
+ color: rgb(25, 25, 25);
220
+ }
221
+
222
+ :deep(button) {
223
+ --tr-icon-button-hover-bg: unset;
224
+ --tr-icon-button-active-bg: unset;
225
+ }
226
+ }
227
+ }
228
+
229
+ // 下拉动画
230
+ .tr-action-group-dropdown {
231
+ &-enter-active,
232
+ &-leave-active {
233
+ transition: opacity 0.3s ease;
234
+ }
235
+
236
+ &-enter-from,
237
+ &-leave-to {
238
+ opacity: 0;
239
+ }
240
+
241
+ &-enter-to,
242
+ &-leave-from {
243
+ opacity: 1;
244
+ }
245
+ }
246
+ }
247
+ </style>
@@ -0,0 +1,9 @@
1
+ <script setup lang="ts">
2
+ import { ActionGroupItemProps } from './index.type'
3
+
4
+ defineProps<ActionGroupItemProps>()
5
+ </script>
6
+
7
+ <template>
8
+ <slot></slot>
9
+ </template>
@@ -0,0 +1,25 @@
1
+ import { App } from 'vue'
2
+ import ActionGroup from './ActionGroup.vue'
3
+ import ActionGroupItemComp from './ActionGroupItem.vue'
4
+
5
+ ActionGroup.name = 'TrActionGroup'
6
+
7
+ const install = function <T>(app: App<T>) {
8
+ app.component(ActionGroup.name!, ActionGroup)
9
+ }
10
+
11
+ ActionGroup.install = install
12
+
13
+ export default ActionGroup as typeof ActionGroup & { install: typeof install }
14
+
15
+ ActionGroupItemComp.name = 'TrActionGroupItem'
16
+
17
+ const installActionGroupItem = function <T>(app: App<T>) {
18
+ app.component(ActionGroupItemComp.name!, ActionGroupItemComp)
19
+ }
20
+
21
+ ActionGroupItemComp.install = installActionGroupItem
22
+
23
+ export const ActionGroupItem = ActionGroupItemComp as typeof ActionGroupItemComp & {
24
+ install: typeof installActionGroupItem
25
+ }
@@ -0,0 +1,21 @@
1
+ import { VNode } from 'vue'
2
+
3
+ export interface ActionGroupProps {
4
+ maxNum?: number
5
+ showTooltip?: boolean
6
+ dropDownShowLabelOnly?: boolean
7
+ }
8
+
9
+ export interface ActionGroupEvents {
10
+ (e: 'item-click', name: string): void
11
+ }
12
+
13
+ export interface ActionGroupSlots {
14
+ default: () => VNode | VNode[]
15
+ moreBtn: () => VNode
16
+ }
17
+
18
+ export interface ActionGroupItemProps {
19
+ name: string
20
+ label: string
21
+ }
@@ -0,0 +1,153 @@
1
+ <script setup lang="ts">
2
+ import markdownit from 'markdown-it'
3
+ import { computed } from 'vue'
4
+ import { BubbleProps, BubbleSlots } from './index.type'
5
+
6
+ const props = withDefaults(defineProps<BubbleProps>(), {
7
+ content: '',
8
+ placement: 'start',
9
+ type: 'text',
10
+ maxWidth: '80%',
11
+ })
12
+
13
+ const slots = defineSlots<BubbleSlots>()
14
+
15
+ const markdownItInstance = computed(() => {
16
+ return markdownit(props.mdConfig || {})
17
+ })
18
+
19
+ const bubbleContent = computed(() => {
20
+ if (props.type === 'markdown') {
21
+ return markdownItInstance.value.render(props.content)
22
+ }
23
+ return props.content
24
+ })
25
+
26
+ const placementStart = computed(() => props.placement === 'start')
27
+ </script>
28
+
29
+ <template>
30
+ <div
31
+ :class="[
32
+ 'tr-bubble',
33
+ {
34
+ 'placement-start': placementStart,
35
+ 'placement-end': !placementStart,
36
+ },
37
+ ]"
38
+ >
39
+ <div v-if="props.avatar" class="tr-bubble__avatar">
40
+ <component :is="props.avatar"></component>
41
+ </div>
42
+ <div class="tr-bubble__content-wrapper">
43
+ <slot v-if="props.loading" name="loading" :bubble-props="props">
44
+ <div class="tr-bubble__loading">
45
+ <span></span>
46
+ <span></span>
47
+ <span></span>
48
+ </div>
49
+ </slot>
50
+ <div v-else :class="['tr-bubble__content']">
51
+ <div class="tr-bubbule__body">
52
+ <slot :bubble-props="props">
53
+ <span v-if="props.type === 'markdown'" v-html="bubbleContent"></span>
54
+ <span v-else>{{ bubbleContent }}</span>
55
+ <span v-if="props.aborted" class="tr-bubbule__aborted">(用户停止)</span>
56
+ </slot>
57
+ </div>
58
+ <div v-if="slots.footer" class="tr-bubbule__footer">
59
+ <slot name="footer" :bubble-props="props"></slot>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </template>
65
+
66
+ <style lang="less" scoped>
67
+ .tr-bubble {
68
+ display: flex;
69
+ gap: 16px;
70
+ max-width: v-bind('props.maxWidth');
71
+
72
+ &.placement-start {
73
+ flex-direction: row;
74
+ }
75
+
76
+ &.placement-end {
77
+ flex-direction: row-reverse;
78
+ margin-left: auto;
79
+ }
80
+ }
81
+
82
+ .tr-bubble__avatar {
83
+ width: 40px;
84
+ height: 40px;
85
+ flex-shrink: 0;
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ }
90
+
91
+ .tr-bubble__loading {
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ padding: 8px 0;
96
+
97
+ span {
98
+ height: 8px;
99
+ width: 8px;
100
+ margin: 0 2px;
101
+ background-color: #bbb;
102
+ border-radius: 50%;
103
+ display: inline-block;
104
+ animation: typing 1.4s infinite ease-in-out both;
105
+
106
+ &:nth-child(1) {
107
+ animation-delay: 0s;
108
+ }
109
+
110
+ &:nth-child(2) {
111
+ animation-delay: 0.2s;
112
+ }
113
+
114
+ &:nth-child(3) {
115
+ animation-delay: 0.4s;
116
+ }
117
+ }
118
+
119
+ @keyframes typing {
120
+ 0%,
121
+ 80%,
122
+ 100% {
123
+ transform: scale(0);
124
+ }
125
+ 40% {
126
+ transform: scale(1);
127
+ }
128
+ }
129
+ }
130
+
131
+ .tr-bubble__content {
132
+ background-color: white;
133
+ padding: 16px 24px;
134
+ border-radius: 24px;
135
+ box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.02);
136
+
137
+ .tr-bubbule__body {
138
+ color: rgb(25, 25, 25);
139
+ font-size: 16px;
140
+ line-height: 26px;
141
+ word-break: break-word;
142
+ }
143
+
144
+ .tr-bubbule__aborted {
145
+ color: rgb(128, 128, 128);
146
+ font-size: 14px;
147
+ }
148
+
149
+ .tr-bubbule__footer {
150
+ margin-top: 12px;
151
+ }
152
+ }
153
+ </style>
@@ -0,0 +1,55 @@
1
+ <script setup lang="ts">
2
+ import { useScroll } from '@vueuse/core'
3
+ import { computed, useTemplateRef, watch } from 'vue'
4
+ import Bubble from './Bubble.vue'
5
+ import { BubbleListProps, BubbleProps, BubbleSlots } from './index.type'
6
+
7
+ const props = withDefaults(defineProps<BubbleListProps>(), {})
8
+
9
+ const scrollContainerRef = useTemplateRef<HTMLDivElement>('scrollContainer')
10
+ const { y } = useScroll(scrollContainerRef, {
11
+ behavior: 'smooth',
12
+ throttle: 100,
13
+ })
14
+ const lastBubble = computed(() => props.items.at(-1))
15
+
16
+ watch([() => props.items.length, () => lastBubble.value?.content], () => {
17
+ if (!props.autoScroll || !scrollContainerRef.value) {
18
+ return
19
+ }
20
+
21
+ y.value = scrollContainerRef.value.scrollHeight
22
+ })
23
+
24
+ const getItemProps = (item: BubbleProps & { slots?: BubbleSlots }): BubbleProps => {
25
+ const defaultConfig = item.role ? props.roles?.[item.role] || {} : {}
26
+ const { slots: _roleSlots, ...rest } = defaultConfig
27
+ const { slots: _itemSlots, ...restItem } = item
28
+ return { ...rest, ...restItem }
29
+ }
30
+
31
+ const getItemSlots = (item: BubbleProps & { slots?: BubbleSlots }): BubbleSlots => {
32
+ const defaultConfig = item.role ? props.roles?.[item.role] || {} : {}
33
+ return { ...defaultConfig.slots, ...item.slots }
34
+ }
35
+ </script>
36
+
37
+ <template>
38
+ <div class="tr-bubble-list" ref="scrollContainer">
39
+ <Bubble v-for="(item, index) in props.items" :key="item.id || index" v-bind="getItemProps(item)">
40
+ <template v-for="(_, slotName) in getItemSlots(item)" #[slotName]="slotProps" :key="slotName">
41
+ <component :is="getItemSlots(item)[slotName]" v-bind="slotProps" />
42
+ </template>
43
+ </Bubble>
44
+ </div>
45
+ </template>
46
+
47
+ <style lang="less" scoped>
48
+ .tr-bubble-list {
49
+ display: flex;
50
+ flex-direction: column;
51
+ gap: 16px;
52
+ overflow-y: auto;
53
+ padding: 16px;
54
+ }
55
+ </style>
@@ -1,6 +1,6 @@
1
1
  import { App } from 'vue'
2
- import BubbleComp from './bubble.vue'
3
- import BubbleListComp from './bubble-list.vue'
2
+ import BubbleComp from './Bubble.vue'
3
+ import BubbleListComp from './BubbleList.vue'
4
4
 
5
5
  BubbleComp.name = 'TrBubble'
6
6
 
@@ -1,13 +1,5 @@
1
1
  import { Options as MarkdownItOptions } from 'markdown-it'
2
- import { Component, CSSProperties, VNode } from 'vue'
3
-
4
- export interface BubbleActionOptions {
5
- name: 'copy' | 'refresh' | string
6
- vnode?: VNode | Component
7
- show?: boolean | ((props: BubbleProps) => boolean)
8
- }
9
-
10
- export type BubbleAction = 'copy' | 'refresh' | BubbleActionOptions
2
+ import { CSSProperties, VNode } from 'vue'
11
3
 
12
4
  export type BubblePalcement = 'start' | 'end'
13
5
 
@@ -33,28 +25,22 @@ export interface BubbleProps {
33
25
  * type 为 'markdown' 时,markdown 的配置项
34
26
  */
35
27
  mdConfig?: MarkdownItOptions
36
-
37
- actions?: BubbleAction[]
38
28
  // 样式相关
39
29
  maxWidth?: CSSProperties['maxWidth']
40
30
  }
41
31
 
42
32
  export interface BubbleSlots {
43
- default: () => unknown
44
- footer: () => unknown
45
- loading: () => unknown
33
+ default?: (slotProps: { bubbleProps: BubbleProps }) => unknown
34
+ footer?: (slotProps: { bubbleProps: BubbleProps }) => unknown
35
+ loading?: (slotProps: { bubbleProps: BubbleProps }) => unknown
46
36
  }
47
37
 
48
- export interface BubbleEvents {
49
- (e: 'copy', result: boolean): void
50
- (e: 'refresh'): void
51
- (e: 'action', name: string, ...args: unknown[]): void
38
+ export type BubbleRoleConfig = Pick<BubbleProps, 'placement' | 'avatar' | 'type' | 'mdConfig' | 'maxWidth'> & {
39
+ slots?: BubbleSlots
52
40
  }
53
41
 
54
- export type BubbleRoleConfig = Pick<BubbleProps, 'placement' | 'avatar' | 'type' | 'mdConfig' | 'actions' | 'maxWidth'>
55
-
56
42
  export interface BubbleListProps {
57
- items: BubbleProps[]
43
+ items: (BubbleProps & { slots?: BubbleSlots })[]
58
44
  /**
59
45
  * 每个角色的默认配置项
60
46
  */
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { IconCancelFullScreen, IconClose, IconFullScreen } from '@opentiny/tiny-robot-svgs'
3
3
  import { computed } from 'vue'
4
+ import IconButton from '../icon-button'
4
5
  import { ContainerProps, ContainerSlots } from './index.type'
5
6
 
6
7
  const show = defineModel<ContainerProps['show']>('show', { required: true })
@@ -22,17 +23,16 @@ const IconFullScreenSwitcher = computed(() => (fullscreen.value ? IconCancelFull
22
23
  </slot>
23
24
  <div class="tr-container__header-operations">
24
25
  <slot name="operations"></slot>
25
- <button class="icon-btn" @click="$emit('update:fullscreen', !fullscreen)">
26
- <IconFullScreenSwitcher />
27
- </button>
28
- <button class="icon-btn" @click="$emit('update:show', false)">
29
- <IconClose />
30
- </button>
26
+ <icon-button
27
+ size="28"
28
+ svg-size="20"
29
+ :icon="IconFullScreenSwitcher"
30
+ @click="$emit('update:fullscreen', !fullscreen)"
31
+ ></icon-button>
32
+ <icon-button size="28" svg-size="20" :icon="IconClose" @click="$emit('update:show', false)"></icon-button>
31
33
  </div>
32
34
  </div>
33
- <div class="tr-container__body">
34
- <slot></slot>
35
- </div>
35
+ <slot></slot>
36
36
  <div class="tr-container__footer">
37
37
  <slot name="footer"></slot>
38
38
  </div>
@@ -95,35 +95,9 @@ const IconFullScreenSwitcher = computed(() => (fullscreen.value ? IconCancelFull
95
95
  .tr-container__header-operations {
96
96
  display: flex;
97
97
  gap: 8px;
98
-
99
- button.icon-btn {
100
- width: 28px;
101
- height: 28px;
102
- display: inline-flex;
103
- align-items: center;
104
- justify-content: center;
105
- border: none;
106
- border-radius: 8px;
107
- cursor: pointer;
108
- padding: 0;
109
- transition: background-color 0.3s;
110
-
111
- &:hover {
112
- background-color: rgba(0, 0, 0, 0.04);
113
- }
114
-
115
- &:active {
116
- background-color: rgba(0, 0, 0, 0.15);
117
- }
118
-
119
- svg {
120
- width: 20px;
121
- height: 20px;
122
- }
123
- }
124
98
  }
125
99
 
126
- .tr-container__body {
100
+ .tr-container__header + :slotted(*) {
127
101
  flex: 1;
128
102
  overflow-y: auto;
129
103
  }