tgo-widget-miniprogram 1.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.
Files changed (55) hide show
  1. package/README.md +81 -0
  2. package/miniprogram_dist/adapters/request.js +38 -0
  3. package/miniprogram_dist/adapters/storage.js +42 -0
  4. package/miniprogram_dist/adapters/systemInfo.js +22 -0
  5. package/miniprogram_dist/chat/index.js +164 -0
  6. package/miniprogram_dist/chat/index.json +7 -0
  7. package/miniprogram_dist/chat/index.wxml +48 -0
  8. package/miniprogram_dist/chat/index.wxss +92 -0
  9. package/miniprogram_dist/components/json-render-element/index.js +374 -0
  10. package/miniprogram_dist/components/json-render-element/index.json +6 -0
  11. package/miniprogram_dist/components/json-render-element/index.wxml +218 -0
  12. package/miniprogram_dist/components/json-render-element/index.wxss +450 -0
  13. package/miniprogram_dist/components/json-render-message/index.js +89 -0
  14. package/miniprogram_dist/components/json-render-message/index.json +7 -0
  15. package/miniprogram_dist/components/json-render-message/index.wxml +25 -0
  16. package/miniprogram_dist/components/json-render-message/index.wxss +26 -0
  17. package/miniprogram_dist/components/json-render-surface/index.js +116 -0
  18. package/miniprogram_dist/components/json-render-surface/index.json +6 -0
  19. package/miniprogram_dist/components/json-render-surface/index.wxml +10 -0
  20. package/miniprogram_dist/components/json-render-surface/index.wxss +6 -0
  21. package/miniprogram_dist/components/markdown-text/index.js +23 -0
  22. package/miniprogram_dist/components/markdown-text/index.json +3 -0
  23. package/miniprogram_dist/components/markdown-text/index.wxml +1 -0
  24. package/miniprogram_dist/components/markdown-text/index.wxss +6 -0
  25. package/miniprogram_dist/components/message-bubble/index.js +12 -0
  26. package/miniprogram_dist/components/message-bubble/index.json +3 -0
  27. package/miniprogram_dist/components/message-bubble/index.wxml +3 -0
  28. package/miniprogram_dist/components/message-bubble/index.wxss +17 -0
  29. package/miniprogram_dist/components/message-input/index.js +76 -0
  30. package/miniprogram_dist/components/message-input/index.json +3 -0
  31. package/miniprogram_dist/components/message-input/index.wxml +28 -0
  32. package/miniprogram_dist/components/message-input/index.wxss +56 -0
  33. package/miniprogram_dist/components/message-list/index.js +113 -0
  34. package/miniprogram_dist/components/message-list/index.json +9 -0
  35. package/miniprogram_dist/components/message-list/index.wxml +108 -0
  36. package/miniprogram_dist/components/message-list/index.wxss +113 -0
  37. package/miniprogram_dist/components/system-message/index.js +8 -0
  38. package/miniprogram_dist/components/system-message/index.json +3 -0
  39. package/miniprogram_dist/components/system-message/index.wxml +3 -0
  40. package/miniprogram_dist/components/system-message/index.wxss +15 -0
  41. package/miniprogram_dist/core/chatStore.js +758 -0
  42. package/miniprogram_dist/core/i18n.js +66 -0
  43. package/miniprogram_dist/core/platformStore.js +86 -0
  44. package/miniprogram_dist/core/types.js +192 -0
  45. package/miniprogram_dist/services/chat.js +67 -0
  46. package/miniprogram_dist/services/messageHistory.js +46 -0
  47. package/miniprogram_dist/services/platform.js +27 -0
  48. package/miniprogram_dist/services/upload.js +74 -0
  49. package/miniprogram_dist/services/visitor.js +67 -0
  50. package/miniprogram_dist/services/wukongim.js +183 -0
  51. package/miniprogram_dist/utils/jsonRender.js +158 -0
  52. package/miniprogram_dist/utils/markdown.js +31 -0
  53. package/miniprogram_dist/utils/time.js +85 -0
  54. package/miniprogram_dist/utils/uid.js +11 -0
  55. package/package.json +37 -0
@@ -0,0 +1,10 @@
1
+ <view wx:if="{{hasRoot}}" class="json-render-surface">
2
+ <json-render-element
3
+ elementKey="{{spec.root}}"
4
+ spec="{{spec}}"
5
+ stateSnapshot="{{stateSnapshot}}"
6
+ depth="{{0}}"
7
+ bind:action="onAction"
8
+ bind:statechange="onStateChange"
9
+ />
10
+ </view>
@@ -0,0 +1,6 @@
1
+ .json-render-surface {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 12rpx;
5
+ margin-top: 8rpx;
6
+ }
@@ -0,0 +1,23 @@
1
+ var markdown = require('../../utils/markdown')
2
+
3
+ Component({
4
+ properties: {
5
+ content: {
6
+ type: String,
7
+ value: '',
8
+ observer: function (val) {
9
+ this.setData({ html: markdown.parseMarkdown(val) })
10
+ }
11
+ }
12
+ },
13
+ data: {
14
+ html: ''
15
+ },
16
+ lifetimes: {
17
+ attached: function () {
18
+ if (this.properties.content) {
19
+ this.setData({ html: markdown.parseMarkdown(this.properties.content) })
20
+ }
21
+ }
22
+ }
23
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "component": true
3
+ }
@@ -0,0 +1 @@
1
+ <rich-text class="markdown-text" nodes="{{html}}" />
@@ -0,0 +1,6 @@
1
+ .markdown-text {
2
+ font-size: 28rpx;
3
+ line-height: 1.6;
4
+ color: #333;
5
+ word-break: break-word;
6
+ }
@@ -0,0 +1,12 @@
1
+ Component({
2
+ properties: {
3
+ self: {
4
+ type: Boolean,
5
+ value: false
6
+ },
7
+ themeColor: {
8
+ type: String,
9
+ value: '#2f80ed'
10
+ }
11
+ }
12
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "component": true
3
+ }
@@ -0,0 +1,3 @@
1
+ <view class="bubble {{self ? 'bubble-self' : 'bubble-agent'}}" style="{{self ? 'background-color:' + themeColor + ';' : ''}}">
2
+ <slot />
3
+ </view>
@@ -0,0 +1,17 @@
1
+ .bubble {
2
+ max-width: 80%;
3
+ padding: 20rpx 24rpx;
4
+ border-radius: 16rpx;
5
+ word-break: break-word;
6
+ }
7
+
8
+ .bubble-self {
9
+ color: #fff;
10
+ border-bottom-right-radius: 4rpx;
11
+ }
12
+
13
+ .bubble-agent {
14
+ background-color: #f5f5f5;
15
+ color: #333;
16
+ border-bottom-left-radius: 4rpx;
17
+ }
@@ -0,0 +1,76 @@
1
+ var i18n = require('../../core/i18n')
2
+
3
+ Component({
4
+ properties: {
5
+ isStreaming: {
6
+ type: Boolean,
7
+ value: false
8
+ },
9
+ streamCanceling: {
10
+ type: Boolean,
11
+ value: false
12
+ },
13
+ themeColor: {
14
+ type: String,
15
+ value: '#2f80ed'
16
+ },
17
+ disabled: {
18
+ type: Boolean,
19
+ value: false
20
+ }
21
+ },
22
+ data: {
23
+ inputValue: '',
24
+ sendLabel: '',
25
+ interruptLabel: '',
26
+ placeholder: ''
27
+ },
28
+ lifetimes: {
29
+ attached: function () {
30
+ this.setData({
31
+ sendLabel: i18n.t('messageInput.send'),
32
+ interruptLabel: i18n.t('messageInput.interrupt'),
33
+ placeholder: i18n.t('messageInput.placeholder')
34
+ })
35
+ }
36
+ },
37
+ methods: {
38
+ onInput: function (e) {
39
+ this.setData({ inputValue: e.detail.value })
40
+ },
41
+
42
+ onConfirm: function () {
43
+ this._doSend()
44
+ },
45
+
46
+ onTapSend: function () {
47
+ if (this.properties.isStreaming) {
48
+ this.triggerEvent('cancel')
49
+ } else {
50
+ this._doSend()
51
+ }
52
+ },
53
+
54
+ _doSend: function () {
55
+ var text = (this.data.inputValue || '').trim()
56
+ if (!text) return
57
+ this.setData({ inputValue: '' })
58
+ this.triggerEvent('send', { text: text })
59
+ },
60
+
61
+ onChooseImage: function () {
62
+ var self = this
63
+ wx.chooseMedia({
64
+ count: 1,
65
+ mediaType: ['image'],
66
+ sourceType: ['album', 'camera'],
67
+ success: function (res) {
68
+ var file = res.tempFiles && res.tempFiles[0]
69
+ if (file) {
70
+ self.triggerEvent('image', { tempFilePath: file.tempFilePath })
71
+ }
72
+ }
73
+ })
74
+ }
75
+ }
76
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "component": true
3
+ }
@@ -0,0 +1,28 @@
1
+ <view class="input-bar">
2
+ <!-- Image picker button -->
3
+ <view class="btn-icon" bindtap="onChooseImage">
4
+ <text class="icon-text">+</text>
5
+ </view>
6
+
7
+ <!-- Text input -->
8
+ <input
9
+ class="text-input"
10
+ value="{{inputValue}}"
11
+ placeholder="{{placeholder}}"
12
+ confirm-type="send"
13
+ bindinput="onInput"
14
+ bindconfirm="onConfirm"
15
+ disabled="{{disabled}}"
16
+ adjust-position="{{true}}"
17
+ cursor-spacing="16"
18
+ />
19
+
20
+ <!-- Send / Stop button -->
21
+ <view
22
+ class="btn-send {{isStreaming ? 'btn-stop' : ''}}"
23
+ style="background-color: {{themeColor}};"
24
+ bindtap="onTapSend"
25
+ >
26
+ <text class="btn-send-text">{{isStreaming ? interruptLabel : sendLabel}}</text>
27
+ </view>
28
+ </view>
@@ -0,0 +1,56 @@
1
+ .input-bar {
2
+ display: flex;
3
+ align-items: center;
4
+ padding: 16rpx 20rpx;
5
+ background: #fff;
6
+ border-top: 1rpx solid #eee;
7
+ }
8
+
9
+ .btn-icon {
10
+ width: 64rpx;
11
+ height: 64rpx;
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ border-radius: 50%;
16
+ background: #f0f0f0;
17
+ margin-right: 12rpx;
18
+ flex-shrink: 0;
19
+ }
20
+
21
+ .icon-text {
22
+ font-size: 36rpx;
23
+ color: #666;
24
+ font-weight: bold;
25
+ }
26
+
27
+ .text-input {
28
+ flex: 1;
29
+ height: 72rpx;
30
+ padding: 0 20rpx;
31
+ font-size: 28rpx;
32
+ background: #f5f5f5;
33
+ border-radius: 36rpx;
34
+ color: #333;
35
+ }
36
+
37
+ .btn-send {
38
+ margin-left: 12rpx;
39
+ padding: 0 28rpx;
40
+ height: 64rpx;
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ border-radius: 32rpx;
45
+ flex-shrink: 0;
46
+ }
47
+
48
+ .btn-send.btn-stop {
49
+ background-color: #e74c3c !important;
50
+ }
51
+
52
+ .btn-send-text {
53
+ font-size: 26rpx;
54
+ color: #fff;
55
+ white-space: nowrap;
56
+ }
@@ -0,0 +1,113 @@
1
+ var types = require('../../core/types')
2
+ var timeUtil = require('../../utils/time')
3
+
4
+ Component({
5
+ properties: {
6
+ messages: {
7
+ type: Array,
8
+ value: [],
9
+ observer: function (newVal) {
10
+ this._processMessages(newVal)
11
+ }
12
+ },
13
+ historyLoading: {
14
+ type: Boolean,
15
+ value: false
16
+ },
17
+ historyHasMore: {
18
+ type: Boolean,
19
+ value: true
20
+ },
21
+ isStreaming: {
22
+ type: Boolean,
23
+ value: false
24
+ },
25
+ themeColor: {
26
+ type: String,
27
+ value: '#2f80ed'
28
+ }
29
+ },
30
+
31
+ data: {
32
+ displayMessages: [],
33
+ scrollTop: 0,
34
+ scrollAnimated: true,
35
+ _prevLen: 0,
36
+ _scrollCounter: 0
37
+ },
38
+
39
+ lifetimes: {
40
+ attached: function () {
41
+ if (this.properties.messages.length) {
42
+ this._processMessages(this.properties.messages)
43
+ }
44
+ }
45
+ },
46
+
47
+ methods: {
48
+ _processMessages: function (messages) {
49
+ var prevLen = this.data._prevLen
50
+ var list = (messages || []).map(function (m) {
51
+ var isSystem = m.payload && types.isSystemMessageType(m.payload.type)
52
+ var hasUiParts = Array.isArray(m.uiParts) && m.uiParts.length > 0
53
+ return {
54
+ id: m.id,
55
+ role: m.role,
56
+ payload: m.payload,
57
+ streamData: m.streamData,
58
+ errorMessage: m.errorMessage,
59
+ status: m.status,
60
+ uploadProgress: m.uploadProgress,
61
+ isSystem: isSystem,
62
+ isSelf: m.role === 'user',
63
+ _time: m.time ? timeUtil.formatMessageTime(m.time) : '',
64
+ uiParts: hasUiParts ? m.uiParts : null,
65
+ hasUiParts: hasUiParts
66
+ }
67
+ })
68
+
69
+ var shouldScroll = false
70
+ if (list.length > prevLen) {
71
+ shouldScroll = true
72
+ } else if (list.length > 0) {
73
+ var last = list[list.length - 1]
74
+ if (last.streamData) {
75
+ shouldScroll = true
76
+ }
77
+ }
78
+
79
+ var setObj = {
80
+ displayMessages: list,
81
+ _prevLen: list.length
82
+ }
83
+
84
+ if (shouldScroll && list.length > 0) {
85
+ // Toggle between two large values to force scroll-top re-trigger
86
+ var c = this.data._scrollCounter + 1
87
+ setObj.scrollTop = 999999 + (c % 2)
88
+ setObj._scrollCounter = c
89
+ setObj.scrollAnimated = !last || !last.streamData
90
+ }
91
+
92
+ this.setData(setObj)
93
+ },
94
+
95
+ onScrollToUpper: function () {
96
+ this.triggerEvent('loadmore')
97
+ },
98
+
99
+ onTapImage: function (e) {
100
+ var url = e.currentTarget.dataset.url
101
+ if (url) {
102
+ wx.previewImage({
103
+ current: url,
104
+ urls: [url]
105
+ })
106
+ }
107
+ },
108
+
109
+ onJsonRenderSend: function (e) {
110
+ this.triggerEvent('sendmessage', e.detail)
111
+ }
112
+ }
113
+ })
@@ -0,0 +1,9 @@
1
+ {
2
+ "component": true,
3
+ "usingComponents": {
4
+ "message-bubble": "../message-bubble/index",
5
+ "markdown-text": "../markdown-text/index",
6
+ "system-message": "../system-message/index",
7
+ "json-render-message": "../json-render-message/index"
8
+ }
9
+ }
@@ -0,0 +1,108 @@
1
+ <scroll-view
2
+ class="msg-list"
3
+ scroll-y
4
+ scroll-top="{{scrollTop}}"
5
+ scroll-with-animation="{{scrollAnimated}}"
6
+ upper-threshold="80"
7
+ bindscrolltoupper="onScrollToUpper"
8
+ >
9
+ <!-- Loading indicator -->
10
+ <view wx:if="{{historyLoading}}" class="status-row">
11
+ <text class="status-text">加载中...</text>
12
+ </view>
13
+ <view wx:elif="{{!historyHasMore}}" class="status-row">
14
+ <text class="status-text-muted">没有更多消息</text>
15
+ </view>
16
+
17
+ <!-- Message rows -->
18
+ <view
19
+ wx:for="{{displayMessages}}"
20
+ wx:key="id"
21
+ id="msg-{{item.id}}"
22
+ class="msg-row"
23
+ >
24
+ <!-- System message -->
25
+ <system-message
26
+ wx:if="{{item.isSystem}}"
27
+ content="{{item.payload.content}}"
28
+ />
29
+
30
+ <!-- Regular message -->
31
+ <view wx:else class="msg-inner {{item.isSelf ? 'msg-self' : 'msg-agent'}}">
32
+
33
+ <!-- Image message -->
34
+ <view
35
+ wx:if="{{item.payload.type === 2}}"
36
+ class="msg-image-wrap"
37
+ bindtap="onTapImage"
38
+ data-url="{{item.payload.url}}"
39
+ >
40
+ <image
41
+ src="{{item.payload.url}}"
42
+ mode="widthFix"
43
+ class="msg-image"
44
+ lazy-load
45
+ />
46
+ </view>
47
+
48
+ <!-- JSON Render message -->
49
+ <block wx:elif="{{item.hasUiParts}}">
50
+ <message-bubble self="{{item.isSelf}}" themeColor="{{themeColor}}">
51
+ <json-render-message
52
+ uiParts="{{item.uiParts}}"
53
+ payloadContent="{{item.payload.content || ''}}"
54
+ showCursor="{{!!item.streamData}}"
55
+ themeColor="{{themeColor}}"
56
+ bind:sendmessage="onJsonRenderSend"
57
+ />
58
+ </message-bubble>
59
+ </block>
60
+
61
+ <!-- Text / streaming / loading / error -->
62
+ <block wx:else>
63
+ <message-bubble self="{{item.isSelf}}" themeColor="{{themeColor}}">
64
+
65
+ <!-- AI Loading (type 100) -->
66
+ <view wx:if="{{item.payload.type === 100 && !item.streamData}}" class="loading-dots">
67
+ <view class="dot dot1"></view>
68
+ <view class="dot dot2"></view>
69
+ <view class="dot dot3"></view>
70
+ </view>
71
+
72
+ <!-- Error -->
73
+ <view wx:elif="{{item.errorMessage}}" class="error-text">
74
+ <text>{{item.errorMessage}}</text>
75
+ </view>
76
+
77
+ <!-- Streaming content -->
78
+ <block wx:elif="{{item.streamData}}">
79
+ <markdown-text content="{{item.streamData}}" />
80
+ <view class="cursor"></view>
81
+ </block>
82
+
83
+ <!-- Normal text -->
84
+ <block wx:elif="{{item.payload.type === 1}}">
85
+ <markdown-text content="{{item.payload.content}}" />
86
+ </block>
87
+
88
+ <!-- Fallback -->
89
+ <block wx:else>
90
+ <text>{{item.payload.content || ''}}</text>
91
+ </block>
92
+
93
+ </message-bubble>
94
+ </block>
95
+
96
+ <!-- Status indicators -->
97
+ <view wx:if="{{item.status === 'sending'}}" class="msg-status">
98
+ <text class="status-text-small">发送中...</text>
99
+ </view>
100
+ <view wx:elif="{{item.status === 'uploading'}}" class="msg-status">
101
+ <text class="status-text-small">上传中 {{item.uploadProgress || 0}}%</text>
102
+ </view>
103
+
104
+ <!-- Time label -->
105
+ <text wx:if="{{item._time}}" class="time-label">{{item._time}}</text>
106
+ </view>
107
+ </view>
108
+ </scroll-view>
@@ -0,0 +1,113 @@
1
+ .msg-list {
2
+ height: 100%;
3
+ padding: 16rpx 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ .status-row {
8
+ display: flex;
9
+ justify-content: center;
10
+ padding: 16rpx;
11
+ }
12
+
13
+ .status-text {
14
+ font-size: 24rpx;
15
+ color: #666;
16
+ }
17
+
18
+ .status-text-muted {
19
+ font-size: 24rpx;
20
+ color: #ccc;
21
+ }
22
+
23
+ .msg-row {
24
+ padding: 8rpx 24rpx;
25
+ }
26
+
27
+ .msg-inner {
28
+ display: flex;
29
+ flex-direction: column;
30
+ }
31
+
32
+ .msg-self {
33
+ align-items: flex-end;
34
+ }
35
+
36
+ .msg-agent {
37
+ align-items: flex-start;
38
+ }
39
+
40
+ .msg-image-wrap {
41
+ max-width: 60%;
42
+ border-radius: 16rpx;
43
+ overflow: hidden;
44
+ }
45
+
46
+ .msg-image {
47
+ width: 100%;
48
+ display: block;
49
+ }
50
+
51
+ /* Loading dots animation */
52
+ .loading-dots {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 8rpx;
56
+ padding: 8rpx 0;
57
+ }
58
+
59
+ .dot {
60
+ width: 12rpx;
61
+ height: 12rpx;
62
+ border-radius: 50%;
63
+ background-color: #999;
64
+ animation: dotBounce 1.4s infinite ease-in-out both;
65
+ }
66
+
67
+ .dot1 { animation-delay: -0.32s; }
68
+ .dot2 { animation-delay: -0.16s; }
69
+ .dot3 { animation-delay: 0s; }
70
+
71
+ @keyframes dotBounce {
72
+ 0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
73
+ 40% { transform: scale(1); opacity: 1; }
74
+ }
75
+
76
+ /* Streaming cursor */
77
+ .cursor {
78
+ display: inline-block;
79
+ width: 4rpx;
80
+ height: 28rpx;
81
+ background: #333;
82
+ margin-left: 4rpx;
83
+ animation: blink 1s step-end infinite;
84
+ vertical-align: text-bottom;
85
+ }
86
+
87
+ @keyframes blink {
88
+ 0%, 100% { opacity: 1; }
89
+ 50% { opacity: 0; }
90
+ }
91
+
92
+ /* Error text */
93
+ .error-text {
94
+ color: #e74c3c;
95
+ font-size: 26rpx;
96
+ }
97
+
98
+ /* Status */
99
+ .msg-status {
100
+ margin-top: 4rpx;
101
+ }
102
+
103
+ .status-text-small {
104
+ font-size: 22rpx;
105
+ color: #999;
106
+ }
107
+
108
+ /* Time label */
109
+ .time-label {
110
+ font-size: 20rpx;
111
+ color: #bbb;
112
+ margin-top: 4rpx;
113
+ }
@@ -0,0 +1,8 @@
1
+ Component({
2
+ properties: {
3
+ content: {
4
+ type: String,
5
+ value: ''
6
+ }
7
+ }
8
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "component": true
3
+ }
@@ -0,0 +1,3 @@
1
+ <view class="system-message">
2
+ <text class="system-text">{{content}}</text>
3
+ </view>
@@ -0,0 +1,15 @@
1
+ .system-message {
2
+ display: flex;
3
+ justify-content: center;
4
+ padding: 16rpx 32rpx;
5
+ }
6
+
7
+ .system-text {
8
+ font-size: 24rpx;
9
+ color: #999;
10
+ background: rgba(0, 0, 0, 0.04);
11
+ padding: 8rpx 24rpx;
12
+ border-radius: 16rpx;
13
+ max-width: 80%;
14
+ text-align: center;
15
+ }