@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,743 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { QrCode } from './QrCode';
11
- const DEFAULT_REMOTE_URL = 'https://agent.opentiny.design/tiny-robot';
12
- const DEFAULT_QR_CODE_URL = 'https://ai.opentiny.design/next-remoter';
13
- const getDefaultMenuItems = (options) => {
14
- return [
15
- {
16
- action: 'qr-code',
17
- show: true,
18
- text: '弹出二维码',
19
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
20
- <rect x="3" y="3" width="6" height="6" rx="1" stroke="currentColor" stroke-width="2" fill="none"/>
21
- <rect x="15" y="3" width="6" height="6" rx="1" stroke="currentColor" stroke-width="2" fill="none"/>
22
- <rect x="3" y="15" width="6" height="6" rx="1" stroke="currentColor" stroke-width="2" fill="none"/>
23
- <line x1="9" y1="6" x2="9" y2="18" stroke="currentColor" stroke-width="1.5"/>
24
- <line x1="15" y1="6" x2="15" y2="18" stroke="currentColor" stroke-width="1.5"/>
25
- <line x1="6" y1="9" x2="18" y2="9" stroke="currentColor" stroke-width="1.5"/>
26
- <line x1="6" y1="15" x2="18" y2="15" stroke="currentColor" stroke-width="1.5"/>
27
- <circle cx="12" cy="12" r="1" fill="currentColor"/>
28
- </svg>`
29
- },
30
- {
31
- action: 'ai-chat',
32
- show: true,
33
- text: '弹出 AI 对话框',
34
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
35
- <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"/>
36
- <path d="M7 9H17V11H7V9Z" fill="currentColor"/>
37
- <path d="M7 12H14V14H7V12Z" fill="currentColor"/>
38
- </svg>`
39
- },
40
- {
41
- action: 'remote-control',
42
- show: true,
43
- text: `识别码:${options.sessionId.slice(-6)}`,
44
- showCopyIcon: true,
45
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
46
- <rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2" fill="none"/>
47
- <rect x="6" y="6" width="3" height="2" rx="0.5" fill="currentColor"/>
48
- <rect x="10" y="6" width="3" height="2" rx="0.5" fill="currentColor"/>
49
- <rect x="14" y="6" width="3" height="2" rx="0.5" fill="currentColor"/>
50
- <rect x="6" y="9" width="3" height="2" rx="0.5" fill="currentColor"/>
51
- <rect x="10" y="9" width="3" height="2" rx="0.5" fill="currentColor"/>
52
- <rect x="14" y="9" width="3" height="2" rx="0.5" fill="currentColor"/>
53
- <rect x="6" y="12" width="3" height="2" rx="0.5" fill="currentColor"/>
54
- <rect x="10" y="12" width="3" height="2" rx="0.5" fill="currentColor"/>
55
- <rect x="14" y="12" width="3" height="2" rx="0.5" fill="currentColor"/>
56
- </svg>`
57
- },
58
- {
59
- action: 'remote-url',
60
- show: true,
61
- text: `${options.remoteUrl}`,
62
- tip: options.remoteUrl,
63
- showCopyIcon: true,
64
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
65
- <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"/>
66
- <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"/>
67
- </svg>`
68
- }
69
- ];
70
- };
71
- class FloatingBlock {
72
- // 计算 sessionPrefix 属性
73
- get sessionPrefix() {
74
- var _a;
75
- return ((_a = this.options.qrCodeUrl) === null || _a === void 0 ? void 0 : _a.includes('?')) ? '&sessionId=' : '?sessionId=';
76
- }
77
- constructor(options) {
78
- this.isExpanded = false;
79
- if (!options.sessionId) {
80
- throw new Error('sessionId is required');
81
- }
82
- this.options = Object.assign(Object.assign({}, options), { qrCodeUrl: options.qrCodeUrl || DEFAULT_QR_CODE_URL, remoteUrl: options.remoteUrl || DEFAULT_REMOTE_URL });
83
- // 合并默认菜单项配置和用户配置
84
- this.menuItems = this.mergeMenuItems(options.menuItems);
85
- this.init();
86
- }
87
- /**
88
- * 合并菜单项配置
89
- * @param userMenuItems 用户自定义菜单项配置
90
- * @returns 合并后的菜单项配置
91
- */
92
- mergeMenuItems(userMenuItems) {
93
- if (!userMenuItems) {
94
- return getDefaultMenuItems(this.options);
95
- }
96
- return getDefaultMenuItems(this.options).map((defaultItem) => {
97
- const userItem = userMenuItems.find((item) => item.action === defaultItem.action);
98
- if (userItem) {
99
- return Object.assign(Object.assign(Object.assign({}, defaultItem), userItem), {
100
- // 确保show属性存在,默认为true
101
- show: userItem.show !== undefined ? userItem.show : defaultItem.show });
102
- }
103
- return defaultItem;
104
- });
105
- }
106
- init() {
107
- this.createFloatingBlock();
108
- this.createDropdownMenu();
109
- this.bindEvents();
110
- this.addStyles();
111
- }
112
- // 创建主浮动块
113
- createFloatingBlock() {
114
- this.floatingBlock = document.createElement('div');
115
- this.floatingBlock.className = 'tiny-remoter-floating-block';
116
- this.floatingBlock.innerHTML = `
117
- <div class="tiny-remoter-floating-block__icon">
118
- <img style="display: block; width: 56px;" src="${DEFAULT_QR_CODE_URL}/svgs/logo-next-no-bg-left.svg" alt="icon" />
119
- </div>
120
- `;
121
- document.body.appendChild(this.floatingBlock);
122
- }
123
- // 创建下拉菜单
124
- createDropdownMenu() {
125
- this.dropdownMenu = document.createElement('div');
126
- this.dropdownMenu.className = 'tiny-remoter-floating-dropdown';
127
- // 根据配置动态生成菜单项
128
- const menuItemsHTML = this.menuItems
129
- .filter((item) => item.show !== false) // 过滤掉show为false的菜单项
130
- .map((item) => `
131
- <div class="tiny-remoter-dropdown-item" data-action="${item.action}">
132
- <div class="tiny-remoter-dropdown-item__icon">
133
- ${item.icon}
134
- </div>
135
- <span title="${item.tip}">${item.text}</span>
136
- ${item.showCopyIcon
137
- ? `
138
- <div class="tiny-remoter-copy-icon" data-action="${item.action}">
139
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
140
- <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"/>
141
- </svg>
142
- </div>
143
- `
144
- : ''}
145
- </div>
146
- `)
147
- .join('');
148
- this.dropdownMenu.innerHTML = menuItemsHTML;
149
- document.body.appendChild(this.dropdownMenu);
150
- }
151
- bindEvents() {
152
- // 绑定浮动块点击事件
153
- this.floatingBlock.addEventListener('click', () => {
154
- this.toggleDropdown();
155
- });
156
- // 绑定菜单项点击事件
157
- this.dropdownMenu.addEventListener('click', (e) => {
158
- const target = e.target;
159
- // 检查是否点击了复制图标
160
- const copyIcon = target.closest('.tiny-remoter-copy-icon');
161
- if (copyIcon) {
162
- e.stopPropagation(); // 阻止事件冒泡,避免触发菜单项点击
163
- const action = copyIcon.dataset.action;
164
- if (action) {
165
- this.handleAction(action);
166
- }
167
- return;
168
- }
169
- // 处理普通菜单项点击
170
- const actionItem = target.closest('.tiny-remoter-dropdown-item');
171
- const action = actionItem === null || actionItem === void 0 ? void 0 : actionItem.dataset.action;
172
- if (action) {
173
- this.handleAction(action);
174
- }
175
- });
176
- // 点击外部关闭菜单
177
- document.addEventListener('click', (e) => {
178
- const target = e.target;
179
- if (!this.floatingBlock.contains(target) && !this.dropdownMenu.contains(target)) {
180
- this.closeDropdown();
181
- }
182
- });
183
- // ESC键关闭菜单
184
- document.addEventListener('keydown', (e) => {
185
- if (e.key === 'Escape') {
186
- this.closeDropdown();
187
- }
188
- });
189
- }
190
- toggleDropdown() {
191
- if (this.isExpanded) {
192
- this.closeDropdown();
193
- }
194
- else {
195
- this.openDropdown();
196
- }
197
- }
198
- openDropdown() {
199
- this.isExpanded = true;
200
- this.floatingBlock.classList.add('expanded');
201
- this.dropdownMenu.classList.add('show');
202
- }
203
- closeDropdown() {
204
- this.isExpanded = false;
205
- this.floatingBlock.classList.remove('expanded');
206
- this.dropdownMenu.classList.remove('show');
207
- }
208
- handleAction(action) {
209
- switch (action) {
210
- case 'qr-code':
211
- this.showQRCode();
212
- break;
213
- case 'ai-chat':
214
- this.showAIChat();
215
- break;
216
- case 'remote-control':
217
- this.copyRemoteControl();
218
- break;
219
- case 'remote-url':
220
- this.copyRemoteURL();
221
- break;
222
- }
223
- this.closeDropdown();
224
- }
225
- copyRemoteControl() {
226
- this.copyToClipboard(this.options.sessionId.slice(-6));
227
- }
228
- copyRemoteURL() {
229
- this.copyToClipboard(this.options.remoteUrl + this.sessionPrefix + this.options.sessionId);
230
- }
231
- // 实现复制到剪贴板功能
232
- copyToClipboard(text) {
233
- return __awaiter(this, void 0, void 0, function* () {
234
- try {
235
- // 优先使用现代浏览器的 Clipboard API
236
- if (navigator.clipboard && window.isSecureContext) {
237
- yield navigator.clipboard.writeText(text);
238
- this.showCopyFeedback(true);
239
- }
240
- else {
241
- // 降级方案:使用传统的 document.execCommand
242
- const textArea = document.createElement('textarea');
243
- textArea.value = text;
244
- textArea.style.position = 'fixed';
245
- textArea.style.left = '-999999px';
246
- textArea.style.top = '-999999px';
247
- document.body.appendChild(textArea);
248
- textArea.focus();
249
- textArea.select();
250
- const successful = document.execCommand('copy');
251
- document.body.removeChild(textArea);
252
- if (successful) {
253
- this.showCopyFeedback(true);
254
- }
255
- else {
256
- this.showCopyFeedback(false);
257
- }
258
- }
259
- }
260
- catch (error) {
261
- console.error('复制失败:', error);
262
- this.showCopyFeedback(false);
263
- }
264
- });
265
- }
266
- // 显示复制反馈提示
267
- showCopyFeedback(success) {
268
- const message = success ? '复制成功!' : '复制失败,请手动复制';
269
- const feedback = document.createElement('div');
270
- feedback.className = `tiny-remoter-copy-feedback ${success ? 'success' : 'error'}`;
271
- feedback.textContent = message;
272
- document.body.appendChild(feedback);
273
- // 添加显示动画
274
- setTimeout(() => feedback.classList.add('show'), 10);
275
- // 3秒后自动移除
276
- setTimeout(() => {
277
- feedback.classList.remove('show');
278
- setTimeout(() => {
279
- if (feedback.parentNode) {
280
- feedback.parentNode.removeChild(feedback);
281
- }
282
- }, 300);
283
- }, 1500);
284
- }
285
- // 创建二维码弹窗
286
- showQRCode() {
287
- return __awaiter(this, void 0, void 0, function* () {
288
- const qrCode = new QrCode((this.options.qrCodeUrl || '') + this.sessionPrefix + this.options.sessionId, {});
289
- const base64 = yield qrCode.toDataURL();
290
- const modal = this.createModal('扫码前往智能遥控器', `
291
- <div style="text-align: center; padding: 32px;">
292
- <!-- 二维码容器 - 添加渐变背景和阴影效果 -->
293
- <div style="
294
- width: 240px;
295
- height: 240px;
296
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
297
- margin: 0 auto 24px;
298
- display: flex;
299
- align-items: center;
300
- justify-content: center;
301
- border-radius: 20px;
302
- box-shadow: 0 20px 40px rgba(102, 126, 234, 0.3);
303
- position: relative;
304
- overflow: hidden;
305
- ">
306
- <!-- 装饰性背景元素 -->
307
- <div style="
308
- position: absolute;
309
- top: -50%;
310
- left: -50%;
311
- width: 200%;
312
- height: 200%;
313
- background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
314
- animation: rotate 20s linear infinite;
315
- "></div>
316
-
317
- <!-- 二维码图片容器 -->
318
- <div style="
319
- width: 200px;
320
- height: 200px;
321
- background: white;
322
- border-radius: 16px;
323
- padding: 16px;
324
- box-shadow: 0 8px 32px rgba(0,0,0,0.1);
325
- position: relative;
326
- z-index: 1;
327
- ">
328
- <img src="${base64}" alt="二维码" style="
329
- width: 100%;
330
- height: 100%;
331
- object-fit: contain;
332
- border-radius: 8px;
333
- ">
334
- </div>
335
- </div>
336
-
337
- <!-- 标题文字 -->
338
- <h3 style="
339
- color: #333;
340
- margin: 0 0 12px 0;
341
- font-size: 20px;
342
- font-weight: 600;
343
- letter-spacing: 0.5px;
344
- ">扫描二维码</h3>
345
-
346
- <!-- 描述文字 -->
347
- <p style="
348
- color: #666;
349
- margin: 0 auto;
350
- margin-bottom: 20px;
351
- font-size: 14px;
352
- line-height: 1.6;
353
- max-width: 280px;
354
- ">请使用手机微信或者浏览器扫描二维码跳转到智能遥控器</p>
355
-
356
- <!-- 提示图标和文字 -->
357
- <div style="
358
- display: flex;
359
- align-items: center;
360
- justify-content: center;
361
- gap: 8px;
362
- color: #999;
363
- font-size: 12px;
364
- ">
365
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="opacity: 0.7;">
366
- <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor"/>
367
- </svg>
368
- <span>支持微信、浏览器等多种方式</span>
369
- </div>
370
- </div>
371
- `);
372
- this.showModal(modal);
373
- });
374
- }
375
- // 创建AI对话弹窗--- 暂时调 “用户函数”
376
- showAIChat() {
377
- var _a, _b;
378
- (_b = (_a = this.options).onShowAIChat) === null || _b === void 0 ? void 0 : _b.call(_a);
379
- }
380
- createModal(title, content) {
381
- const modal = document.createElement('div');
382
- modal.className = 'tiny-remoter-floating-modal';
383
- modal.innerHTML = `
384
- <div class="tiny-remoter-modal-overlay"></div>
385
- <div class="tiny-remoter-modal-content">
386
- <div class="tiny-remoter-modal-header">
387
- <h3>${title}</h3>
388
- <button class="tiny-remoter-modal-close">&times;</button>
389
- </div>
390
- <div class="tiny-remoter-modal-body">
391
- ${content}
392
- </div>
393
- </div>
394
- `;
395
- // 绑定关闭事件
396
- const closeBtn = modal.querySelector('.tiny-remoter-modal-close');
397
- const overlay = modal.querySelector('.tiny-remoter-modal-overlay');
398
- closeBtn.addEventListener('click', () => this.hideModal(modal));
399
- overlay.addEventListener('click', () => this.hideModal(modal));
400
- return modal;
401
- }
402
- showModal(modal) {
403
- document.body.appendChild(modal);
404
- // 添加显示动画
405
- setTimeout(() => modal.classList.add('show'), 10);
406
- }
407
- hideModal(modal) {
408
- modal.classList.remove('show');
409
- setTimeout(() => {
410
- if (modal.parentNode) {
411
- modal.parentNode.removeChild(modal);
412
- }
413
- }, 100);
414
- }
415
- // 创建样式表
416
- addStyles() {
417
- const style = document.createElement('style');
418
- style.textContent = `
419
- /* 浮动块样式 */
420
- .tiny-remoter-floating-block {
421
- position: fixed;
422
- bottom: 30px;
423
- right: 30px;
424
- width: 60px;
425
- height: 60px;
426
- cursor: pointer;
427
- display: flex;
428
- align-items: center;
429
- justify-content: center;
430
- color: white;
431
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
432
- z-index: 99;
433
- overflow: hidden;
434
- border-radius: 50%;
435
- }
436
-
437
- .tiny-remoter-floating-block__icon {
438
- transform: scale(0.8);
439
- transition: transform 0.3s ease;
440
- }
441
-
442
- .tiny-remoter-floating-block__icon:hover {
443
- transform: scale(1.1);
444
- }
445
-
446
- .tiny-remoter-floating-block.expanded .tiny-remoter-floating-block__icon {
447
- transform: scale(1.1);
448
- }
449
-
450
- /* 下拉菜单样式 */
451
- .tiny-remoter-floating-dropdown {
452
- position: fixed;
453
- bottom: 100px;
454
- right: 30px;
455
- background: white;
456
- border-radius: 16px;
457
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
458
- padding: 8px;
459
- opacity: 0;
460
- visibility: hidden;
461
- transform: translateY(20px) scale(0.95);
462
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
463
- z-index: 999;
464
- min-width: 200px;
465
- }
466
-
467
- .tiny-remoter-floating-dropdown.show {
468
- opacity: 1;
469
- visibility: visible;
470
- transform: translateY(0) scale(1);
471
- }
472
-
473
- .tiny-remoter-dropdown-item {
474
- display: flex;
475
- align-items: center;
476
- gap: 12px;
477
- padding: 12px 16px;
478
- border-radius: 12px;
479
- cursor: pointer;
480
- transition: all 0.2s ease;
481
- color: #333;
482
- }
483
-
484
- .tiny-remoter-dropdown-item > span {
485
- flex: 1;
486
- overflow: hidden;
487
- max-width: 120px;
488
- text-overflow: ellipsis;
489
- white-space: nowrap;
490
- }
491
-
492
- .tiny-remoter-dropdown-item:hover {
493
- background: #f8f9fa;
494
- transform: translateX(4px);
495
- }
496
-
497
- .tiny-remoter-dropdown-item__icon {
498
- display: flex;
499
- align-items: center;
500
- justify-content: center;
501
- width: 32px;
502
- height: 32px;
503
- background: #f8f9fa;
504
- border-radius: 8px;
505
- color: #667eea;
506
- }
507
-
508
- /* 复制图标样式 */
509
- .tiny-remoter-copy-icon {
510
- display: flex;
511
- align-items: center;
512
- justify-content: center;
513
- width: 24px;
514
- height: 24px;
515
- background: #f0f0f0;
516
- border-radius: 6px;
517
- color: #666;
518
- cursor: pointer;
519
- transition: all 0.2s ease;
520
- opacity: 0.7;
521
- margin-left: auto;
522
- }
523
-
524
- .tiny-remoter-copy-icon:hover {
525
- background: #e0e0e0;
526
- color: #333;
527
- opacity: 1;
528
- transform: scale(1.1);
529
- }
530
-
531
- .tiny-remoter-copy-icon:active {
532
- transform: scale(0.95);
533
- }
534
-
535
- /* 弹窗样式 */
536
- .tiny-remoter-floating-modal {
537
- position: fixed;
538
- top: 0;
539
- left: 0;
540
- width: 100%;
541
- height: 100%;
542
- z-index: 2000;
543
- display: flex;
544
- align-items: center;
545
- justify-content: center;
546
- }
547
-
548
- .tiny-remoter-modal-overlay {
549
- position: absolute;
550
- top: 0;
551
- left: 0;
552
- width: 100%;
553
- height: 100%;
554
- background: rgba(0, 0, 0, 0.5);
555
- backdrop-filter: blur(4px);
556
- opacity: 0;
557
- transition: opacity 0.3s ease;
558
- }
559
-
560
- .tiny-remoter-modal-content {
561
- background: white;
562
- border-radius: 16px;
563
- box-shadow: 0 25px 80px rgba(0, 0, 0, 0.2);
564
- max-width: 500px;
565
- width: 90%;
566
- max-height: 80vh;
567
- overflow: hidden;
568
- transform: scale(0.9) translateY(20px);
569
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
570
- }
571
-
572
- .tiny-remoter-floating-modal.show .tiny-remoter-modal-overlay {
573
- opacity: 1;
574
- }
575
-
576
- .tiny-remoter-floating-modal.show .tiny-remoter-modal-content {
577
- transform: scale(1) translateY(0);
578
- }
579
-
580
- .tiny-remoter-modal-header {
581
- display: flex;
582
- align-items: center;
583
- justify-content: space-between;
584
- padding: 20px 24px;
585
- border-bottom: 1px solid #f0f0f0;
586
- }
587
-
588
- .tiny-remoter-modal-header h3 {
589
- margin: 0;
590
- font-size: 18px;
591
- font-weight: 600;
592
- color: #333;
593
- }
594
-
595
- .tiny-remoter-modal-close {
596
- background: none;
597
- border: none;
598
- font-size: 24px;
599
- cursor: pointer;
600
- color: #999;
601
- padding: 0;
602
- width: 32px;
603
- height: 32px;
604
- border-radius: 50%;
605
- display: flex;
606
- align-items: center;
607
- justify-content: center;
608
- transition: all 0.2s ease;
609
- }
610
-
611
- .tiny-remoter-modal-close:hover {
612
- background: #f5f5f5;
613
- color: #666;
614
- }
615
-
616
- .tiny-remoter-modal-body {
617
- padding: 24px;
618
- }
619
-
620
- /* 二维码弹窗动画 */
621
- @keyframes rotate {
622
- from {
623
- transform: rotate(0deg);
624
- }
625
- to {
626
- transform: rotate(360deg);
627
- }
628
- }
629
-
630
- /* 响应式设计 */
631
- @media (max-width: 768px) {
632
- .tiny-remoter-floating-block {
633
- bottom: 20px;
634
- right: 20px;
635
- width: 56px;
636
- height: 56px;
637
- }
638
-
639
- .tiny-remoter-floating-dropdown {
640
- bottom: 90px;
641
- right: 20px;
642
- min-width: 180px;
643
- }
644
-
645
- .tiny-remoter-modal-content {
646
- width: 95%;
647
- margin: 20px;
648
- }
649
- }
650
-
651
- /* 复制反馈提示样式 */
652
- .tiny-remoter-copy-feedback {
653
- position: fixed;
654
- top: 50%;
655
- left: 50%;
656
- transform: translate(-50%, -50%);
657
- background: rgba(0, 0, 0, 0.8);
658
- color: white;
659
- padding: 12px 24px;
660
- border-radius: 8px;
661
- font-size: 14px;
662
- font-weight: 500;
663
- z-index: 10000;
664
- opacity: 0;
665
- visibility: hidden;
666
- transition: all 0.3s ease;
667
- backdrop-filter: blur(4px);
668
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
669
- }
670
-
671
- .tiny-remoter-copy-feedback.show {
672
- opacity: 1;
673
- visibility: visible;
674
- transform: translate(-50%, -50%) scale(1);
675
- }
676
-
677
- .tiny-remoter-copy-feedback.success {
678
- background: rgba(34, 197, 94, 0.9);
679
- }
680
-
681
- .tiny-remoter-copy-feedback.error {
682
- background: rgba(239, 68, 68, 0.9);
683
- }
684
-
685
- /* 深色主题支持 */
686
- @media (prefers-color-scheme: dark) {
687
- .tiny-remoter-floating-dropdown {
688
- background: #1a1a1a;
689
- color: white;
690
- }
691
-
692
- .tiny-remoter-dropdown-item {
693
- color: white;
694
- }
695
-
696
- .tiny-remoter-dropdown-item:hover {
697
- background: #2a2a2a;
698
- }
699
-
700
- .tiny-remoter-dropdown-item__icon {
701
- background: #2a2a2a;
702
- }
703
-
704
- .tiny-remoter-copy-icon {
705
- background: #2a2a2a;
706
- color: #ccc;
707
- }
708
-
709
- .tiny-remoter-copy-icon:hover {
710
- background: #3a3a3a;
711
- color: white;
712
- }
713
-
714
- .tiny-remoter-modal-content {
715
- background: #1a1a1a;
716
- color: white;
717
- }
718
-
719
- .tiny-remoter-modal-header {
720
- border-bottom-color: #333;
721
- }
722
-
723
- .tiny-remoter-modal-header h3 {
724
- color: white;
725
- }
726
- }
727
- `;
728
- document.head.appendChild(style);
729
- }
730
- // 销毁组件
731
- destroy() {
732
- if (this.floatingBlock.parentNode) {
733
- this.floatingBlock.parentNode.removeChild(this.floatingBlock);
734
- }
735
- if (this.dropdownMenu.parentNode) {
736
- this.dropdownMenu.parentNode.removeChild(this.dropdownMenu);
737
- }
738
- }
739
- }
740
- // 导出组件
741
- export const createRemoter = (options = {}) => {
742
- return new FloatingBlock(options);
743
- };