@opentiny/next-sdk 0.1.14 → 0.1.15-beta.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 (77) hide show
  1. package/agent/AgentModelProvider.ts +546 -22
  2. package/agent/type.ts +12 -6
  3. package/agent/utils/generateReActPrompt.ts +55 -0
  4. package/agent/utils/parseReActAction.ts +34 -0
  5. package/dist/McpSdk.d.ts +14 -0
  6. package/dist/WebAgent.d.ts +5 -0
  7. package/dist/WebMcp.d.ts +20 -0
  8. package/dist/WebMcpClient.d.ts +389 -1152
  9. package/dist/WebMcpServer.d.ts +79 -78
  10. package/dist/Zod.d.ts +1 -0
  11. package/dist/agent/AgentModelProvider.d.ts +40 -4
  12. package/dist/agent/type.d.ts +13 -3
  13. package/dist/agent/utils/generateReActPrompt.d.ts +9 -0
  14. package/dist/agent/utils/getAISDKTools.d.ts +1 -0
  15. package/dist/agent/utils/parseReActAction.d.ts +14 -0
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.es.dev.js +36353 -34348
  18. package/dist/index.es.js +28821 -25466
  19. package/dist/index.js +3880 -25
  20. package/dist/index.umd.dev.js +34230 -32225
  21. package/dist/index.umd.js +220 -128
  22. package/dist/mcpsdk@1.24.3.dev.js +22539 -0
  23. package/dist/mcpsdk@1.24.3.es.dev.js +22537 -0
  24. package/dist/mcpsdk@1.24.3.es.js +16781 -0
  25. package/dist/mcpsdk@1.24.3.js +43 -0
  26. package/dist/remoter/createRemoter.d.ts +9 -0
  27. package/dist/remoter/tooltips.d.ts +36 -0
  28. package/dist/script/utils.d.ts +1 -0
  29. package/dist/transport/ExtensionClientTransport.d.ts +3 -2
  30. package/dist/transport/ExtensionContentServerTransport.d.ts +3 -2
  31. package/dist/transport/ExtensionPageServerTransport.d.ts +4 -4
  32. package/dist/vite-build-tsc.d.ts +2 -0
  33. package/dist/vite.config.d.ts +2 -0
  34. package/dist/vite.config.mcpSdk.d.ts +2 -0
  35. package/dist/vite.config.webAgent.d.ts +2 -0
  36. package/dist/vite.config.webMcp.d.ts +2 -0
  37. package/dist/vite.config.webMcpFull.d.ts +2 -0
  38. package/dist/vite.config.zod.d.ts +2 -0
  39. package/dist/webagent.dev.js +24569 -20836
  40. package/dist/webagent.es.dev.js +23907 -20174
  41. package/dist/webagent.es.js +25326 -20723
  42. package/dist/webagent.js +209 -110
  43. package/dist/webmcp-full.dev.js +21225 -20021
  44. package/dist/webmcp-full.es.dev.js +21223 -20019
  45. package/dist/webmcp-full.es.js +16710 -14437
  46. package/dist/webmcp-full.js +42 -15
  47. package/dist/webmcp.dev.js +14 -22
  48. package/dist/webmcp.es.dev.js +12 -20
  49. package/dist/webmcp.es.js +172 -179
  50. package/dist/webmcp.js +1 -1
  51. package/dist/zod@3.25.76.dev.js +30 -32
  52. package/dist/zod@3.25.76.es.dev.js +28 -30
  53. package/dist/zod@3.25.76.es.js +143 -145
  54. package/dist/zod@3.25.76.js +1 -1
  55. package/package.json +11 -9
  56. package/remoter/createRemoter.ts +126 -71
  57. package/remoter/tooltips.ts +260 -0
  58. package/transport/ExtensionPageServerTransport.ts +2 -4
  59. package/tsconfig.json +5 -3
  60. package/vite-build-tsc.ts +60 -0
  61. package/vite-env.d.ts +5 -0
  62. package/dist/WebMcpClient.js +0 -363
  63. package/dist/WebMcpServer.js +0 -283
  64. package/dist/agent/AgentModelProvider.js +0 -293
  65. package/dist/agent/type.js +0 -1
  66. package/dist/agent/utils/getAISDKTools.js +0 -36
  67. package/dist/mcpsdk@1.17.0.dev.js +0 -21391
  68. package/dist/mcpsdk@1.17.0.es.dev.js +0 -21389
  69. package/dist/mcpsdk@1.17.0.es.js +0 -14505
  70. package/dist/mcpsdk@1.17.0.js +0 -16
  71. package/dist/remoter/QrCode.js +0 -55
  72. package/dist/remoter/createRemoter.js +0 -743
  73. package/dist/transport/ExtensionClientTransport.js +0 -81
  74. package/dist/transport/ExtensionContentServerTransport.js +0 -128
  75. package/dist/transport/ExtensionPageServerTransport.js +0 -118
  76. package/dist/transport/messages.js +0 -51
  77. package/dist/utils/uuid.js +0 -10
@@ -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
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
47
- <rect x="3" y="3" width="6" height="6" rx="1" stroke="currentColor" stroke-width="2" fill="none"/>
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: '弹出 AI 对话框',
61
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
62
- <path d="M20 2H4C2.9 2 2 2.9 2 4V22L6 18H20C21.1 18 22 17.1 22 16V4C22 2.9 21.1 2 20 2ZM20 16H6L4 18V4H20V16Z" fill="currentColor"/>
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-control',
69
+ action: 'remote-url',
69
70
  show: true,
70
- text: `识别码:${options.sessionId.slice(-6)}`,
71
+ text: `遥控器链接`,
72
+ desc: `${options.remoteUrl}`,
73
+ active: true,
74
+ tip: options.remoteUrl,
71
75
  showCopyIcon: true,
72
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
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-url',
79
+ action: 'remote-control',
87
80
  show: true,
88
- text: `${options.remoteUrl}`,
89
- tip: options.remoteUrl,
81
+ text: `识别码`,
82
+ desc: `${options.sessionId.slice(-6)}`,
83
+ know: true,
90
84
  showCopyIcon: true,
91
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
92
- <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
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
- <span title="${item.tip}">${item.text}</span>
187
- ${
188
- item.showCopyIcon
189
- ? `
190
- <div class="tiny-remoter-copy-icon" data-action="${item.action}">
191
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
192
- <path d="M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z" fill="currentColor"/>
193
- </svg>
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: 16px;
564
+ border-radius: 18px;
535
565
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
536
- padding: 8px;
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: 32px;
580
- height: 32px;
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
+ })();
@@ -28,7 +28,6 @@ export class ExtensionPageServerTransport implements Transport {
28
28
 
29
29
  // 会话ID,用于标识此 transport 实例并路由消息
30
30
  readonly sessionId: string
31
- readonly tabId: number
32
31
 
33
32
  // 内部状态
34
33
  private _messageListener1: () => void
@@ -47,10 +46,9 @@ export class ExtensionPageServerTransport implements Transport {
47
46
  }
48
47
  }
49
48
 
50
- constructor(sessionId: string | null = null, tabId: number) {
49
+ constructor(sessionId: string | null = null) {
51
50
  // 如果提供了 sessionId,使用提供的;否则随机生成
52
51
  this.sessionId = sessionId || randomUUID()
53
- this.tabId = tabId
54
52
 
55
53
  this._messageListener1 = onWindowMessage(
56
54
  'sidepanel-ready-to-page',
@@ -67,7 +65,7 @@ export class ExtensionPageServerTransport implements Transport {
67
65
  this._messageListener2 = onWindowMessage(
68
66
  'mcp-client-to-server-to-page',
69
67
  (data) => {
70
- if (data.sessionId !== this.sessionId || data.tabId !== this.tabId) return
68
+ if (data.sessionId !== this.sessionId) return
71
69
 
72
70
  console.log('【Page Svr Transport】 即将处理 mcpMessage', data.mcpMessage)
73
71
  const mcpMessage = JSONRPCMessageSchema.parse(data.mcpMessage)
package/tsconfig.json CHANGED
@@ -5,10 +5,12 @@
5
5
  "target": "ES2015",
6
6
  "lib": ["ES2015", "DOM"],
7
7
  "moduleResolution": "node",
8
- "module": "es6",
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": ["index.ts"]
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
+ })
package/vite-env.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /// <reference types="vite/client" />
2
+ declare module '*.svg?url' {
3
+ const src: string;
4
+ export default src;
5
+ }