@opentiny/next-sdk 0.1.14 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/AgentModelProvider.ts +49 -20
- package/agent/type.ts +4 -4
- package/dist/McpSdk.d.ts +14 -0
- package/dist/WebAgent.d.ts +5 -0
- package/dist/WebMcp.d.ts +20 -0
- package/dist/WebMcpClient.d.ts +250 -1152
- package/dist/WebMcpServer.d.ts +190 -78
- package/dist/Zod.d.ts +1 -0
- package/dist/agent/AgentModelProvider.d.ts +6 -4
- package/dist/agent/type.d.ts +6 -2
- package/dist/agent/utils/getAISDKTools.d.ts +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.es.dev.js +21741 -23457
- package/dist/index.es.js +24309 -23733
- package/dist/index.js +1789 -25
- package/dist/index.umd.dev.js +21720 -23436
- package/dist/index.umd.js +184 -132
- package/dist/{mcpsdk@1.17.0.dev.js → mcpsdk@1.23.0.dev.js} +14784 -15255
- package/dist/{mcpsdk@1.17.0.es.dev.js → mcpsdk@1.23.0.es.dev.js} +14787 -15258
- package/dist/mcpsdk@1.23.0.es.js +15584 -0
- package/dist/mcpsdk@1.23.0.js +43 -0
- package/dist/remoter/createRemoter.d.ts +9 -0
- package/dist/remoter/tooltips.d.ts +36 -0
- package/dist/script/utils.d.ts +1 -0
- package/dist/transport/ExtensionClientTransport.d.ts +3 -2
- package/dist/transport/ExtensionContentServerTransport.d.ts +3 -2
- package/dist/transport/ExtensionPageServerTransport.d.ts +3 -2
- package/dist/vite-build-tsc.d.ts +2 -0
- package/dist/vite.config.d.ts +2 -0
- package/dist/vite.config.mcpSdk.d.ts +2 -0
- package/dist/vite.config.webAgent.d.ts +2 -0
- package/dist/vite.config.webMcp.d.ts +2 -0
- package/dist/vite.config.webMcpFull.d.ts +2 -0
- package/dist/vite.config.zod.d.ts +2 -0
- package/dist/webagent.dev.js +18780 -18491
- package/dist/webagent.es.dev.js +18455 -18166
- package/dist/webagent.es.js +22389 -20343
- package/dist/webagent.js +172 -113
- package/dist/webmcp-full.dev.js +14943 -15356
- package/dist/webmcp-full.es.dev.js +14959 -15372
- package/dist/webmcp-full.es.js +13785 -12666
- package/dist/webmcp-full.js +43 -16
- package/package.json +3 -2
- package/remoter/createRemoter.ts +126 -71
- package/remoter/tooltips.ts +260 -0
- package/tsconfig.json +5 -3
- package/vite-build-tsc.ts +60 -0
- package/vite-env.d.ts +5 -0
- package/dist/WebMcpClient.js +0 -363
- package/dist/WebMcpServer.js +0 -283
- package/dist/agent/AgentModelProvider.js +0 -293
- package/dist/agent/type.js +0 -1
- package/dist/agent/utils/getAISDKTools.js +0 -36
- package/dist/mcpsdk@1.17.0.es.js +0 -14505
- package/dist/mcpsdk@1.17.0.js +0 -16
- package/dist/remoter/QrCode.js +0 -55
- package/dist/remoter/createRemoter.js +0 -743
- package/dist/transport/ExtensionClientTransport.js +0 -81
- package/dist/transport/ExtensionContentServerTransport.js +0 -128
- package/dist/transport/ExtensionPageServerTransport.js +0 -118
- package/dist/transport/messages.js +0 -51
- package/dist/utils/uuid.js +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentiny/next-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Chunhui Mo",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"typescript": "^5.2.2",
|
|
24
24
|
"vite": "^5.0.8",
|
|
25
|
+
"vite-plugin-dts": "^3.9.1",
|
|
25
26
|
"@types/qrcode": "^1.5.5",
|
|
26
27
|
"@types/node": "^24.3.0"
|
|
27
28
|
},
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
},
|
|
31
32
|
"scripts": {
|
|
32
33
|
"build:all": "pnpm build:cdn && pnpm build:cdn:dev && pnpm build",
|
|
33
|
-
"build": "tsc",
|
|
34
|
+
"build": "vite build --config vite-build-tsc.ts",
|
|
34
35
|
"build:cdn": "vite build && pnpm build:webAgent && pnpm build:webMcp && pnpm build:mcpSdk && pnpm build:zod && pnpm build:webMcpFull",
|
|
35
36
|
"build:cdn:dev": "vite build --mode dev && pnpm build:webAgent:dev && pnpm build:webMcp:dev && pnpm build:mcpSdk:dev && pnpm build:zod:dev && pnpm build:webMcpFull:dev",
|
|
36
37
|
"build:webAgent": "vite build --config vite.config.webAgent.ts",
|
package/remoter/createRemoter.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { QrCode } from './QrCode'
|
|
2
|
+
import { Tooltip } from './tooltips';
|
|
3
|
+
import chat from './svgs/chat.svg?url';
|
|
4
|
+
import scan from './svgs/scan.svg?url';
|
|
5
|
+
import link from './svgs/link.svg?url';
|
|
6
|
+
import qrCode from './svgs/qrcode.svg?url';
|
|
7
|
+
import iconCopy from './svgs/icon-copy.svg?url';
|
|
2
8
|
|
|
3
9
|
const DEFAULT_REMOTE_URL = 'https://agent.opentiny.design/tiny-robot'
|
|
4
10
|
const DEFAULT_QR_CODE_URL = 'https://ai.opentiny.design/next-remoter'
|
|
@@ -11,6 +17,12 @@ export interface MenuItemConfig {
|
|
|
11
17
|
show?: boolean
|
|
12
18
|
/** 菜单项文本 */
|
|
13
19
|
text?: string
|
|
20
|
+
/** 菜单文字颜色 */
|
|
21
|
+
active?: boolean
|
|
22
|
+
/** 识别码 */
|
|
23
|
+
know?: boolean
|
|
24
|
+
/** 菜单项描述 */
|
|
25
|
+
desc?: string
|
|
14
26
|
/** 菜单项提示 */
|
|
15
27
|
tip?: string
|
|
16
28
|
/** 菜单项图标SVG */
|
|
@@ -42,57 +54,36 @@ const getDefaultMenuItems = (options: FloatingBlockOptions): MenuItemConfig[] =>
|
|
|
42
54
|
{
|
|
43
55
|
action: 'qr-code',
|
|
44
56
|
show: true,
|
|
45
|
-
text: '
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<rect x="15" y="3" width="6" height="6" rx="1" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
49
|
-
<rect x="3" y="15" width="6" height="6" rx="1" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
50
|
-
<line x1="9" y1="6" x2="9" y2="18" stroke="currentColor" stroke-width="1.5"/>
|
|
51
|
-
<line x1="15" y1="6" x2="15" y2="18" stroke="currentColor" stroke-width="1.5"/>
|
|
52
|
-
<line x1="6" y1="9" x2="18" y2="9" stroke="currentColor" stroke-width="1.5"/>
|
|
53
|
-
<line x1="6" y1="15" x2="18" y2="15" stroke="currentColor" stroke-width="1.5"/>
|
|
54
|
-
<circle cx="12" cy="12" r="1" fill="currentColor"/>
|
|
55
|
-
</svg>`
|
|
57
|
+
text: '扫码登录',
|
|
58
|
+
desc: '使用手机遥控页面',
|
|
59
|
+
icon: qrCode
|
|
56
60
|
},
|
|
57
61
|
{
|
|
58
62
|
action: 'ai-chat',
|
|
59
63
|
show: true,
|
|
60
|
-
text: '
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<path d="M7 9H17V11H7V9Z" fill="currentColor"/>
|
|
64
|
-
<path d="M7 12H14V14H7V12Z" fill="currentColor"/>
|
|
65
|
-
</svg>`
|
|
64
|
+
text: '打开对话框',
|
|
65
|
+
desc: '支持在网页端操作AI',
|
|
66
|
+
icon: chat
|
|
66
67
|
},
|
|
67
68
|
{
|
|
68
|
-
action: 'remote-
|
|
69
|
+
action: 'remote-url',
|
|
69
70
|
show: true,
|
|
70
|
-
text:
|
|
71
|
+
text: `遥控器链接`,
|
|
72
|
+
desc: `${options.remoteUrl}`,
|
|
73
|
+
active: true,
|
|
74
|
+
tip: options.remoteUrl,
|
|
71
75
|
showCopyIcon: true,
|
|
72
|
-
icon:
|
|
73
|
-
<rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
74
|
-
<rect x="6" y="6" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
75
|
-
<rect x="10" y="6" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
76
|
-
<rect x="14" y="6" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
77
|
-
<rect x="6" y="9" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
78
|
-
<rect x="10" y="9" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
79
|
-
<rect x="14" y="9" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
80
|
-
<rect x="6" y="12" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
81
|
-
<rect x="10" y="12" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
82
|
-
<rect x="14" y="12" width="3" height="2" rx="0.5" fill="currentColor"/>
|
|
83
|
-
</svg>`
|
|
76
|
+
icon: link
|
|
84
77
|
},
|
|
85
78
|
{
|
|
86
|
-
action: 'remote-
|
|
79
|
+
action: 'remote-control',
|
|
87
80
|
show: true,
|
|
88
|
-
text:
|
|
89
|
-
|
|
81
|
+
text: `识别码`,
|
|
82
|
+
desc: `${options.sessionId.slice(-6)}`,
|
|
83
|
+
know: true,
|
|
90
84
|
showCopyIcon: true,
|
|
91
|
-
icon:
|
|
92
|
-
|
|
93
|
-
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
94
|
-
</svg>`
|
|
95
|
-
}
|
|
85
|
+
icon: scan
|
|
86
|
+
},
|
|
96
87
|
]
|
|
97
88
|
}
|
|
98
89
|
|
|
@@ -125,6 +116,27 @@ class FloatingBlock {
|
|
|
125
116
|
this.init()
|
|
126
117
|
}
|
|
127
118
|
|
|
119
|
+
private getImageUrl = (asset: string | undefined): HTMLImageElement | undefined => {
|
|
120
|
+
if (!asset) return;
|
|
121
|
+
const img = new Image();
|
|
122
|
+
img.src = asset;
|
|
123
|
+
return img;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private renderItem = (): void => {
|
|
127
|
+
this.menuItems
|
|
128
|
+
.filter((item) => item.show !== false) // 过滤掉show为false的菜单项
|
|
129
|
+
.map(
|
|
130
|
+
(item) => {
|
|
131
|
+
const wrapper = document.getElementById(`tiny-remoter-icon-item-${item.action}`) as HTMLDivElement;
|
|
132
|
+
if (!wrapper) return;
|
|
133
|
+
wrapper.innerHTML = '';
|
|
134
|
+
const img = this.getImageUrl(item.icon);
|
|
135
|
+
if (img) wrapper.appendChild(img);
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
128
140
|
/**
|
|
129
141
|
* 合并菜单项配置
|
|
130
142
|
* @param userMenuItems 用户自定义菜单项配置
|
|
@@ -169,6 +181,18 @@ class FloatingBlock {
|
|
|
169
181
|
document.body.appendChild(this.floatingBlock)
|
|
170
182
|
}
|
|
171
183
|
|
|
184
|
+
private readyTips = (el: string): void => {
|
|
185
|
+
const element = document.getElementById(el)
|
|
186
|
+
if (!element) {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
new Tooltip(element, {
|
|
190
|
+
content: '复制',
|
|
191
|
+
placement: 'top',
|
|
192
|
+
trigger: 'hover'
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
172
196
|
// 创建下拉菜单
|
|
173
197
|
private createDropdownMenu(): void {
|
|
174
198
|
this.dropdownMenu = document.createElement('div')
|
|
@@ -180,31 +204,37 @@ class FloatingBlock {
|
|
|
180
204
|
.map(
|
|
181
205
|
(item) => `
|
|
182
206
|
<div class="tiny-remoter-dropdown-item" data-action="${item.action}">
|
|
183
|
-
<div class="tiny-remoter-dropdown-item__icon">
|
|
184
|
-
${item.icon}
|
|
207
|
+
<div id="tiny-remoter-icon-item-${item.action}" class="tiny-remoter-dropdown-item__icon">
|
|
185
208
|
</div>
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
?
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
209
|
+
<div class="tiny-remoter-dropdown-item__content">
|
|
210
|
+
<div title="${item.tip}">${item.text}</div>
|
|
211
|
+
<div class="tiny-remoter-dropdown-item__desc-wrapper">
|
|
212
|
+
<div class="tiny-remoter-dropdown-item__desc ${item.active ? 'tiny-remoter-dropdown-item__desc--active' : ''} ${item.know ? 'tiny-remoter-dropdown-item__desc--know' : ''}">${item.desc}</div>
|
|
213
|
+
<div>
|
|
214
|
+
${item.showCopyIcon
|
|
215
|
+
? `
|
|
216
|
+
<div class="tiny-remoter-copy-icon" id="${item.action}" data-action="${item.action}">
|
|
217
|
+
<img src="${iconCopy}"/>
|
|
218
|
+
</div>
|
|
219
|
+
`
|
|
220
|
+
: ''
|
|
221
|
+
}
|
|
222
|
+
</div>
|
|
194
223
|
</div>
|
|
195
|
-
|
|
196
|
-
: ''
|
|
197
|
-
}
|
|
224
|
+
</div>
|
|
198
225
|
</div>
|
|
199
226
|
`
|
|
200
227
|
)
|
|
201
228
|
.join('')
|
|
202
229
|
|
|
203
230
|
this.dropdownMenu.innerHTML = menuItemsHTML
|
|
204
|
-
|
|
205
231
|
document.body.appendChild(this.dropdownMenu)
|
|
232
|
+
this.renderItem()
|
|
233
|
+
this.readyTips(`remote-control`)
|
|
234
|
+
this.readyTips(`remote-url`)
|
|
206
235
|
}
|
|
207
236
|
|
|
237
|
+
|
|
208
238
|
private bindEvents(): void {
|
|
209
239
|
// 绑定浮动块点击事件
|
|
210
240
|
this.floatingBlock.addEventListener('click', () => {
|
|
@@ -531,15 +561,17 @@ class FloatingBlock {
|
|
|
531
561
|
bottom: 100px;
|
|
532
562
|
right: 30px;
|
|
533
563
|
background: white;
|
|
534
|
-
border-radius:
|
|
564
|
+
border-radius: 18px;
|
|
535
565
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
|
|
536
|
-
padding:
|
|
566
|
+
padding: 24px 24px 0px 24px;
|
|
537
567
|
opacity: 0;
|
|
538
568
|
visibility: hidden;
|
|
539
569
|
transform: translateY(20px) scale(0.95);
|
|
540
570
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
541
571
|
z-index: 999;
|
|
542
572
|
min-width: 200px;
|
|
573
|
+
width: 223px;
|
|
574
|
+
height: auto;
|
|
543
575
|
}
|
|
544
576
|
|
|
545
577
|
.tiny-remoter-floating-dropdown.show {
|
|
@@ -551,12 +583,16 @@ class FloatingBlock {
|
|
|
551
583
|
.tiny-remoter-dropdown-item {
|
|
552
584
|
display: flex;
|
|
553
585
|
align-items: center;
|
|
554
|
-
gap: 12px;
|
|
555
|
-
padding: 12px 16px;
|
|
556
586
|
border-radius: 12px;
|
|
557
587
|
cursor: pointer;
|
|
558
588
|
transition: all 0.2s ease;
|
|
559
589
|
color: #333;
|
|
590
|
+
margin-bottom: 24px;
|
|
591
|
+
height: 40px;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.tiny-remoter-dropdown-item:last-child {
|
|
595
|
+
margin-bottom: 32px;
|
|
560
596
|
}
|
|
561
597
|
|
|
562
598
|
.tiny-remoter-dropdown-item > span {
|
|
@@ -567,22 +603,48 @@ class FloatingBlock {
|
|
|
567
603
|
white-space: nowrap;
|
|
568
604
|
}
|
|
569
605
|
|
|
570
|
-
.tiny-remoter-dropdown-item:hover {
|
|
571
|
-
background: #f8f9fa;
|
|
572
|
-
transform: translateX(4px);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
606
|
.tiny-remoter-dropdown-item__icon {
|
|
576
607
|
display: flex;
|
|
577
608
|
align-items: center;
|
|
578
609
|
justify-content: center;
|
|
579
|
-
width:
|
|
580
|
-
height:
|
|
610
|
+
width: 40px;
|
|
611
|
+
height: 40px;
|
|
581
612
|
background: #f8f9fa;
|
|
582
613
|
border-radius: 8px;
|
|
583
614
|
color: #667eea;
|
|
584
615
|
}
|
|
585
616
|
|
|
617
|
+
.tiny-remoter-dropdown-item__content {
|
|
618
|
+
flex: 1;
|
|
619
|
+
display: flex;
|
|
620
|
+
flex-direction: column;
|
|
621
|
+
gap: 4px;
|
|
622
|
+
overflow: hidden;
|
|
623
|
+
font-size: 12px;
|
|
624
|
+
margin-left: 16px;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.tiny-remoter-dropdown-item__desc-wrapper {
|
|
628
|
+
display: flex;
|
|
629
|
+
align-items: center;
|
|
630
|
+
gap: 4px;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.tiny-remoter-dropdown-item__desc {
|
|
634
|
+
color: #808080;
|
|
635
|
+
overflow: hidden;
|
|
636
|
+
white-space: nowrap;
|
|
637
|
+
text-overflow: ellipsis;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.tiny-remoter-dropdown-item__desc--active {
|
|
641
|
+
color: #1476ff;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
.tiny-remoter-dropdown-item__desc--know {
|
|
645
|
+
color: #191919;
|
|
646
|
+
}
|
|
647
|
+
|
|
586
648
|
/* 复制图标样式 */
|
|
587
649
|
.tiny-remoter-copy-icon {
|
|
588
650
|
display: flex;
|
|
@@ -771,13 +833,6 @@ class FloatingBlock {
|
|
|
771
833
|
color: white;
|
|
772
834
|
}
|
|
773
835
|
|
|
774
|
-
.tiny-remoter-dropdown-item:hover {
|
|
775
|
-
background: #2a2a2a;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
.tiny-remoter-dropdown-item__icon {
|
|
779
|
-
background: #2a2a2a;
|
|
780
|
-
}
|
|
781
836
|
|
|
782
837
|
.tiny-remoter-copy-icon {
|
|
783
838
|
background: #2a2a2a;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
type Placement =
|
|
2
|
+
| 'top' | 'top-start' | 'top-end'
|
|
3
|
+
| 'bottom' | 'bottom-start' | 'bottom-end'
|
|
4
|
+
| 'left' | 'left-start' | 'left-end'
|
|
5
|
+
| 'right' | 'right-start' | 'right-end'
|
|
6
|
+
|
|
7
|
+
type Trigger = 'hover' | 'focus' | 'click'
|
|
8
|
+
|
|
9
|
+
interface Options {
|
|
10
|
+
content?: string | (() => string)
|
|
11
|
+
placement?: Placement
|
|
12
|
+
trigger?: Trigger
|
|
13
|
+
delay?: number
|
|
14
|
+
hideDelay?: number
|
|
15
|
+
container?: HTMLElement
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DEFAULTS: Required<Options> = {
|
|
20
|
+
content: '',
|
|
21
|
+
placement: 'top',
|
|
22
|
+
trigger: 'hover',
|
|
23
|
+
delay: 150,
|
|
24
|
+
hideDelay: 150,
|
|
25
|
+
container: document.body,
|
|
26
|
+
className: 'tiny-remoter-native-tooltip'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class Tooltip {
|
|
30
|
+
private el: HTMLElement
|
|
31
|
+
private opts: Required<Options>
|
|
32
|
+
private tip: HTMLDivElement | null = null
|
|
33
|
+
private showTimer = 0
|
|
34
|
+
private hideTimer = 0
|
|
35
|
+
private clickOutside: ((e: MouseEvent) => void) | null = null
|
|
36
|
+
|
|
37
|
+
constructor (el: HTMLElement | string, options: Options = {}) {
|
|
38
|
+
this.el = typeof el === 'string' ? document.querySelector(el)! : el
|
|
39
|
+
if (!this.el) throw new Error('Tooltip: invalid element')
|
|
40
|
+
this.opts = { ...DEFAULTS, ...options }
|
|
41
|
+
this.bindEvents()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* 公开 API */
|
|
45
|
+
public open (): void {
|
|
46
|
+
if (this.isShown()) return
|
|
47
|
+
this.clearTimer()
|
|
48
|
+
this.showTimer = window.setTimeout(() => {
|
|
49
|
+
this.render()
|
|
50
|
+
this.opts.container.appendChild(this.tip!)
|
|
51
|
+
this.reposition()
|
|
52
|
+
this.attachExtraEvents()
|
|
53
|
+
}, this.opts.delay)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public close (): void {
|
|
57
|
+
this.clearTimer()
|
|
58
|
+
this.hideTimer = window.setTimeout(() => {
|
|
59
|
+
this.tip?.remove()
|
|
60
|
+
this.detachExtraEvents()
|
|
61
|
+
}, this.opts.hideDelay)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public toggle (): void {
|
|
65
|
+
this.isShown() ? this.close() : this.open()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public destroy (): void {
|
|
69
|
+
this.close()
|
|
70
|
+
const t = this.opts.trigger
|
|
71
|
+
this.el.removeEventListener('mouseenter', this.open)
|
|
72
|
+
this.el.removeEventListener('mouseleave', this.close)
|
|
73
|
+
this.el.removeEventListener('focus', this.open)
|
|
74
|
+
this.el.removeEventListener('blur', this.close)
|
|
75
|
+
if (t === 'click') this.el.removeEventListener('click', this.toggle)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* 私有方法 */
|
|
79
|
+
private bindEvents (): void {
|
|
80
|
+
const t = this.opts.trigger
|
|
81
|
+
if (t === 'hover') {
|
|
82
|
+
this.el.addEventListener('mouseenter', () => this.open())
|
|
83
|
+
this.el.addEventListener('mouseleave', () => this.close())
|
|
84
|
+
} else if (t === 'focus') {
|
|
85
|
+
this.el.addEventListener('focus', () => this.open())
|
|
86
|
+
this.el.addEventListener('blur', () => this.close())
|
|
87
|
+
} else if (t === 'click') {
|
|
88
|
+
this.el.addEventListener('click', () => this.toggle())
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private render (): void {
|
|
93
|
+
if (this.tip) return
|
|
94
|
+
const content = typeof this.opts.content === 'function'
|
|
95
|
+
? this.opts.content()
|
|
96
|
+
: this.opts.content
|
|
97
|
+
this.tip = document.createElement('div')
|
|
98
|
+
this.tip.className = `${this.opts.className} ${this.opts.className}--${this.opts.placement}`
|
|
99
|
+
this.tip.innerHTML = `
|
|
100
|
+
<div class="${this.opts.className}__arrow"></div>
|
|
101
|
+
<div class="${this.opts.className}__inner">${content}</div>
|
|
102
|
+
`
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private reposition (): void {
|
|
106
|
+
const list = this.placementList(this.opts.placement)
|
|
107
|
+
for (const pl of list) {
|
|
108
|
+
const style = this.calcStyle(pl)
|
|
109
|
+
if (this.checkViewport(style)) {
|
|
110
|
+
this.applyStyle(style)
|
|
111
|
+
this.tip!.className = `${this.opts.className} ${this.opts.className}--${pl}`
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
this.applyStyle(this.calcStyle('top'))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private calcStyle (placement: Placement): { top: number, left: number } {
|
|
119
|
+
const tr = this.el.getBoundingClientRect()
|
|
120
|
+
const tp = this.tip!.getBoundingClientRect()
|
|
121
|
+
const st = window.pageYOffset || document.documentElement.scrollTop
|
|
122
|
+
const sl = window.pageXOffset || document.documentElement.scrollLeft
|
|
123
|
+
const arrow = 6
|
|
124
|
+
|
|
125
|
+
const [main, sub = 'center'] = placement.split('-') as [string, string | undefined]
|
|
126
|
+
let top = 0, left = 0
|
|
127
|
+
|
|
128
|
+
if (main === 'top') top = tr.top + st - tp.height - arrow
|
|
129
|
+
else if (main === 'bottom') top = tr.bottom + st + arrow
|
|
130
|
+
else if (main === 'left') left = tr.left + sl - tp.width - arrow
|
|
131
|
+
else if (main === 'right') left = tr.right + sl + arrow
|
|
132
|
+
|
|
133
|
+
if (main === 'top' || main === 'bottom') {
|
|
134
|
+
if (sub === 'start') left = tr.left + sl
|
|
135
|
+
else if (sub === 'end') left = tr.right + sl - tp.width
|
|
136
|
+
else left = (tr.left + tr.right) / 2 + sl - tp.width / 2
|
|
137
|
+
}
|
|
138
|
+
if (main === 'left' || main === 'right') {
|
|
139
|
+
if (sub === 'start') top = tr.top + st
|
|
140
|
+
else if (sub === 'end') top = tr.bottom + st - tp.height
|
|
141
|
+
else top = (tr.top + tr.bottom) / 2 + st - tp.height / 2
|
|
142
|
+
}
|
|
143
|
+
return { top: Math.round(top), left: Math.round(left) }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private applyStyle ({ top, left }: { top: number, left: number }): void {
|
|
147
|
+
Object.assign(this.tip!.style, {
|
|
148
|
+
position: 'absolute',
|
|
149
|
+
top: `${top}px`,
|
|
150
|
+
left: `${left}px`
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private checkViewport ({ top, left }: { top: number, left: number }): boolean {
|
|
155
|
+
const pad = 5
|
|
156
|
+
const w = window.innerWidth
|
|
157
|
+
const h = window.innerHeight
|
|
158
|
+
const tp = this.tip!.getBoundingClientRect()
|
|
159
|
+
return left >= pad && top >= pad &&
|
|
160
|
+
left + tp.width <= w - pad &&
|
|
161
|
+
top + tp.height <= h - pad
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private placementList (pl: Placement): Placement[] {
|
|
165
|
+
const map: Record<string, Placement[]> = {
|
|
166
|
+
top: ['top', 'bottom', 'top-start', 'bottom-start', 'top-end', 'bottom-end'],
|
|
167
|
+
bottom: ['bottom', 'top', 'bottom-start', 'top-start', 'bottom-end', 'top-end'],
|
|
168
|
+
left: ['left', 'right', 'left-start', 'right-start', 'left-end', 'right-end'],
|
|
169
|
+
right: ['right', 'left', 'right-start', 'left-start', 'right-end', 'left-end']
|
|
170
|
+
}
|
|
171
|
+
return map[pl.split('-')[0]] || map.top
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private attachExtraEvents (): void {
|
|
175
|
+
if (this.opts.trigger === 'click') {
|
|
176
|
+
this.clickOutside = (e: MouseEvent) => {
|
|
177
|
+
const path = e.composedPath() as EventTarget[]
|
|
178
|
+
if (!path.includes(this.el) && !path.includes(this.tip!)) this.close()
|
|
179
|
+
}
|
|
180
|
+
document.addEventListener('mousedown', this.clickOutside)
|
|
181
|
+
} else if (this.opts.trigger === 'hover') {
|
|
182
|
+
this.tip!.addEventListener('mouseenter', () => this.clearTimer())
|
|
183
|
+
this.tip!.addEventListener('mouseleave', () => this.close())
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private detachExtraEvents (): void {
|
|
188
|
+
if (this.clickOutside) {
|
|
189
|
+
document.removeEventListener('mousedown', this.clickOutside)
|
|
190
|
+
this.clickOutside = null
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private clearTimer (): void {
|
|
195
|
+
clearTimeout(this.showTimer)
|
|
196
|
+
clearTimeout(this.hideTimer)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private isShown (): boolean {
|
|
200
|
+
return Boolean(this.tip?.parentNode)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* ===== 样式内联 ===== */
|
|
205
|
+
const STYLE_ID = 'tiny-remoter-native-tooltip-style';
|
|
206
|
+
|
|
207
|
+
(() => {
|
|
208
|
+
if (document.getElementById(STYLE_ID)) return; // 已注入就跳过
|
|
209
|
+
const style = document.createElement('style');
|
|
210
|
+
style.id = STYLE_ID;
|
|
211
|
+
style.textContent = `
|
|
212
|
+
.tiny-remoter-native-tooltip {
|
|
213
|
+
position: absolute;
|
|
214
|
+
z-index: 9999;
|
|
215
|
+
max-width: 200px;
|
|
216
|
+
padding: 6px 10px;
|
|
217
|
+
font-size: 12px;
|
|
218
|
+
line-height: 1.4;
|
|
219
|
+
color: #fff;
|
|
220
|
+
background: #191919;
|
|
221
|
+
border-radius: 4px;
|
|
222
|
+
pointer-events: none;
|
|
223
|
+
box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.2);
|
|
224
|
+
}
|
|
225
|
+
.tiny-remoter-native-tooltip__arrow {
|
|
226
|
+
position: absolute;
|
|
227
|
+
width: 0; height: 0;
|
|
228
|
+
border: 6px solid transparent;
|
|
229
|
+
}
|
|
230
|
+
.tiny-remoter-native-tooltip--top
|
|
231
|
+
.tiny-remoter-native-tooltip__arrow {
|
|
232
|
+
bottom: -12px;
|
|
233
|
+
left: 50%; transform:
|
|
234
|
+
translateX(-50%);
|
|
235
|
+
border-top-color: rgba(0,0,0,.75);
|
|
236
|
+
}
|
|
237
|
+
.tiny-remoter-native-tooltip--bottom
|
|
238
|
+
.tiny-remoter-native-tooltip__arrow {
|
|
239
|
+
top: -12px;
|
|
240
|
+
left: 50%; transform:
|
|
241
|
+
translateX(-50%);
|
|
242
|
+
border-bottom-color: rgba(0,0,0,.75);
|
|
243
|
+
}
|
|
244
|
+
.tiny-remoter-native-tooltip--left
|
|
245
|
+
.tiny-remoter-native-tooltip__arrow {
|
|
246
|
+
right: -12px;
|
|
247
|
+
top: 50%;
|
|
248
|
+
transform: translateY(-50%);
|
|
249
|
+
border-left-color: rgba(0,0,0,.75);
|
|
250
|
+
}
|
|
251
|
+
.tiny-remoter-native-tooltip--right
|
|
252
|
+
.tiny-remoter-native-tooltip__arrow {
|
|
253
|
+
left: -12px;
|
|
254
|
+
top: 50%;
|
|
255
|
+
transform: translateY(-50%);
|
|
256
|
+
border-right-color: rgba(0,0,0,.75);
|
|
257
|
+
}
|
|
258
|
+
`;
|
|
259
|
+
document.head.appendChild(style);
|
|
260
|
+
})();
|
package/tsconfig.json
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
"target": "ES2015",
|
|
6
6
|
"lib": ["ES2015", "DOM"],
|
|
7
7
|
"moduleResolution": "node",
|
|
8
|
-
"module": "
|
|
8
|
+
"module": "esnext",
|
|
9
9
|
"strict": true,
|
|
10
10
|
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"types": ["vite/client"]
|
|
12
13
|
},
|
|
13
|
-
"include": ["
|
|
14
|
+
"include": ["**/*.ts"],
|
|
15
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
|
14
16
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import dts from 'vite-plugin-dts'
|
|
3
|
+
|
|
4
|
+
// https://vitejs.dev/config/
|
|
5
|
+
export default defineConfig(() => {
|
|
6
|
+
// 需要排除的第三方依赖列表
|
|
7
|
+
const externalDependencies = [
|
|
8
|
+
'@modelcontextprotocol/sdk',
|
|
9
|
+
'@opentiny/next',
|
|
10
|
+
'@ai-sdk/openai',
|
|
11
|
+
'@ai-sdk/deepseek',
|
|
12
|
+
'@ai-sdk/provider',
|
|
13
|
+
'qrcode',
|
|
14
|
+
'zod',
|
|
15
|
+
'ajv',
|
|
16
|
+
'ai'
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
plugins: [
|
|
21
|
+
// 配置 dts 插件生成类型声明文件
|
|
22
|
+
dts({
|
|
23
|
+
// 指定 TypeScript 配置文件路径
|
|
24
|
+
tsconfigPath: './tsconfig.json',
|
|
25
|
+
outDir: 'dist',
|
|
26
|
+
// 包含所有 TypeScript 文件,确保所有被引用的文件都被处理
|
|
27
|
+
include: ['**/*.ts'],
|
|
28
|
+
exclude: ['node_modules/**', 'dist/**', '**/*.test.ts', '**/*.spec.ts'],
|
|
29
|
+
// 不合并类型文件,保持文件结构以便相对路径引用正常工作
|
|
30
|
+
rollupTypes: false,
|
|
31
|
+
// 插入类型入口文件引用
|
|
32
|
+
insertTypesEntry: true
|
|
33
|
+
})
|
|
34
|
+
],
|
|
35
|
+
build: {
|
|
36
|
+
emptyOutDir: false,
|
|
37
|
+
lib: {
|
|
38
|
+
entry: 'index.ts',
|
|
39
|
+
name: 'NEXT-SDK',
|
|
40
|
+
formats: ['es'],
|
|
41
|
+
fileName: () => 'index.js'
|
|
42
|
+
},
|
|
43
|
+
rollupOptions: {
|
|
44
|
+
// 排除第三方依赖,保留本地文件
|
|
45
|
+
external: (id) => {
|
|
46
|
+
// 如果是相对路径导入(本地文件),不排除
|
|
47
|
+
if (id.startsWith('.') || id.startsWith('/')) {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
// 排除 node_modules 中的第三方依赖
|
|
51
|
+
if (id.includes('node_modules')) {
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
// 排除指定的第三方依赖包
|
|
55
|
+
return externalDependencies.some((dep) => id === dep || id.startsWith(`${dep}/`))
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|