devicely 2.2.10 → 2.2.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devicely",
3
- "version": "2.2.10",
3
+ "version": "2.2.12",
4
4
  "description": "Devicely - One Command, All Devices. AI Powered Mobile Automation for iOS and Android",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -10,8 +10,10 @@
10
10
  "test": "echo \"Error: no test specified\" && exit 1",
11
11
  "sync": "./sync.sh",
12
12
  "test:encryption": "./scripts/test-encryption.sh",
13
- "build": "npm run sync && npm run test:encryption && node scripts/build-simple.js",
14
- "build:obfuscated": "npm run sync && npm run test:encryption && node scripts/build.js",
13
+ "build": "npm run sync && npm run test:encryption && node scripts/build-simple.js && node scripts/cleanup-dist.js",
14
+ "build:obfuscated": "npm run sync && npm run test:encryption && node scripts/build.js && node scripts/cleanup-dist.js",
15
+ "cleanup": "node scripts/cleanup-dist.js",
16
+ "check-size": "./scripts/check-size.sh",
15
17
  "compile:shell": "node scripts/compile-shell-scripts.js",
16
18
  "verify:protection": "cd .. && ./verify-shell-protection.sh",
17
19
  "postinstall": "node scripts/postinstall.js",
@@ -61,6 +63,7 @@
61
63
  "axios": "^1.13.4",
62
64
  "chalk": "^4.1.2",
63
65
  "commander": "^11.1.0",
66
+ "compression": "^1.8.1",
64
67
  "cors": "^2.8.5",
65
68
  "dotenv": "^16.3.1",
66
69
  "express": "^4.18.2",
@@ -1,301 +0,0 @@
1
- // AI Provider Abstraction Layer
2
- // Supports: Google Gemini, GitHub Copilot
3
-
4
- const { OpenAI } = require('openai');
5
- const { GoogleGenerativeAI } = require('@google/generative-ai');
6
- const { getPackageId, APP_MAPPINGS } = require('./appMappings');
7
-
8
- class AIProviderManager {
9
- constructor() {
10
- this.provider = process.env.AI_PROVIDER || 'gemini'; // gemini, copilot
11
- this.initializeProviders();
12
- }
13
-
14
- initializeProviders() {
15
- // Google Gemini
16
- if (process.env.GEMINI_API_KEY) {
17
- this.gemini = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
18
- }
19
-
20
- // GitHub Copilot
21
- if (process.env.GITHUB_TOKEN) {
22
- this.copilot = new OpenAI({
23
- apiKey: process.env.GITHUB_TOKEN,
24
- baseURL: 'https://api.githubcopilot.com',
25
- });
26
- }
27
- }
28
-
29
- getSystemPrompt(platform = null) {
30
- // Generate app list based on platform
31
- let appListSection = '';
32
-
33
- if (platform === 'both') {
34
- // Multi-platform mode - use generic app names
35
- const commonApps = Object.keys(APP_MAPPINGS)
36
- .filter(app => APP_MAPPINGS[app].ios && APP_MAPPINGS[app].android)
37
- .slice(0, 30);
38
-
39
- appListSection = `\n\nMULTI-PLATFORM MODE (iOS + Android devices)
40
- Available apps: ${commonApps.join(', ')}
41
-
42
- IMPORTANT: Use generic app names (e.g., "launch settings", "launch chrome")
43
- The system will automatically convert to platform-specific package IDs:
44
- ${commonApps.slice(0, 15).map(app => `- ${app} → iOS: ${APP_MAPPINGS[app].ios} / Android: ${APP_MAPPINGS[app].android}`).join('\n')}
45
-
46
- Commands will execute SIMULTANEOUSLY on all devices with correct package IDs.
47
- `;
48
- } else if (platform) {
49
- const availableApps = Object.keys(APP_MAPPINGS)
50
- .filter(app => APP_MAPPINGS[app][platform])
51
- .slice(0, 50);
52
-
53
- appListSection = `\n\nPLATFORM: ${platform.toUpperCase()}
54
- Available apps for ${platform}: ${availableApps.join(', ')}
55
-
56
- APP PACKAGE MAPPINGS:
57
- When user says "launch chrome", "open chrome", etc., use the correct package ID:
58
- ${availableApps.slice(0, 20).map(app => `- ${app} → launch ${APP_MAPPINGS[app][platform]}`).join('\n')}
59
- `;
60
- }
61
-
62
- return `You are a mobile device automation command converter. Convert natural language requests into executable commands for iOS and Android devices.
63
-
64
- Available commands (work on both iOS & Android):
65
- - launch <app_name>: Launch an app using generic name (e.g., "launch settings", "launch chrome")
66
- - kill <app_name>: Close/force stop an app
67
- - home: Go to home screen (press home button)
68
- - back: Navigate back (Android/iOS)
69
- - url <url>: Open URL in browser
70
- - click <text>: Click on a button or element by visible text
71
- - click <x,y>: Click at specific coordinates (e.g., click 500,1000)
72
- - tap <text/coords>: Same as click
73
- - longpress <text/coords>: Long press on element or coordinates
74
- - swipe <direction>: Swipe up/down/left/right (use for scrolling)
75
- - type <text>: Type text into focused field (just the text, no "type" prefix)
76
- - screenshot: Take screenshot
77
- - restart: Restart device
78
- - rotate <left/right/portrait/landscape>: Rotate screen
79
-
80
- iOS-specific commands:
81
- - darkmode/lightmode: Change appearance
82
- - airplane <on/off>: Toggle airplane mode
83
- - wifi <on/off>: Toggle WiFi
84
- - volume <up/down/mute>: Control volume
85
-
86
- Android-specific commands:
87
- - getLocators: Get all interactive elements on current screen
88
- - recent: Open recent apps
89
- - notifications: Open notification panel
90
- - quicksettings: Open quick settings
91
- ${appListSection}
92
-
93
- COMMON PHRASE MAPPINGS:
94
- - "scroll up" OR "scroll down" → swipe up OR swipe down
95
- - "go to <url>" OR "open <url>" OR "visit <url>" → url https://<url>
96
- - "press home" OR "go home" OR "home button" → home
97
- - "open settings" OR "launch settings" → launch settings
98
- - "open camera" OR "launch camera" → launch camera
99
-
100
- Examples:
101
- - "open chrome" → launch chrome
102
- - "launch settings" → launch settings
103
- - "open camera" → launch camera
104
- - "scroll up" → swipe up
105
- - "scroll down" → swipe down
106
- - "click on the login button" → click Login
107
- - "tap at center of screen" → click 540,1000
108
- - "swipe down" → swipe down
109
- - "type hello world" → hello world
110
- - "take a screenshot" → screenshot
111
- - "go back" → back
112
- - "press home" → home
113
- - "go to google.com" → url https://www.google.com
114
- - "visit youtube.com" → url https://www.youtube.com
115
- - "launch Chrome and search google.com" → launch chrome
116
- WAIT 3000
117
- url https://www.google.com
118
- - "launch settings scroll up launch camera go to google.com press home" →
119
- launch settings
120
- WAIT 3000
121
- swipe up
122
- WAIT 1000
123
- home
124
- WAIT 500
125
- launch camera
126
- WAIT 3000
127
- home
128
- WAIT 500
129
- url https://www.google.com
130
- WAIT 2000
131
- home
132
-
133
- Convert this request to commands: "{INPUT}"
134
-
135
- CRITICAL RULES - YOU MUST FOLLOW THESE EXACTLY:
136
- 1. Output ONLY executable commands, one per line
137
- 2. NO explanations, NO markdown code blocks, NO comments, NO extra text
138
- 3. For multi-step actions, insert WAIT <milliseconds> between commands
139
- 4. Use GENERIC app names (e.g., "launch settings", "launch chrome", "launch camera")
140
- 5. Do NOT use platform-specific package IDs - use simple app names
141
- 6. The system will automatically convert to correct package IDs for each platform
142
- 5. For URLs, always use: url https://example.com
143
- 6. For scrolling, use: swipe up OR swipe down (never "scroll")
144
- 7. For text input, output ONLY the text (never include "type" prefix)
145
- 8. For home button, output: home (never "press home" or "go home")
146
- 9. WAIT timings: apps=3000ms, pages=2000ms, UI=1000ms, quick=500ms
147
- 10. Parse compound requests into individual steps with WAIT between each
148
-
149
- OUTPUT FORMAT EXAMPLE:
150
- launch com.apple.Preferences
151
- WAIT 3000
152
- swipe up
153
- WAIT 1000
154
- home
155
-
156
- DO NOT include any other text. Start your response with the first command.`;
157
- }
158
-
159
- async convertCommand(text, platform = null, providerOverride = null) {
160
- const provider = providerOverride || this.provider;
161
-
162
- try {
163
- switch (provider) {
164
- case 'gemini':
165
- return await this.convertWithGemini(text, platform);
166
- case 'copilot':
167
- return await this.convertWithCopilot(text, platform);
168
- default:
169
- throw new Error(`Unknown AI provider: ${provider}`);
170
- }
171
- } catch (error) {
172
- console.error(`Error with ${provider}:`, error.message);
173
- // Re-throw the error instead of silently falling back
174
- // This ensures the user is aware of AI failures
175
- throw new Error(`AI conversion failed (${provider}): ${error.message}`);
176
- }
177
- }
178
-
179
- async convertWithGemini(text, platform = null) {
180
- if (!this.gemini) {
181
- throw new Error('Gemini not configured');
182
- }
183
-
184
- // Use Gemini 2.5 Flash (current stable model)
185
- const model = this.gemini.getGenerativeModel({
186
- model: process.env.GEMINI_MODEL || 'gemini-2.5-flash'
187
- });
188
-
189
- const prompt = this.getSystemPrompt(platform).replace('{INPUT}', text);
190
- const result = await model.generateContent(prompt);
191
- const response = await result.response;
192
- let convertedText = response.text().trim();
193
-
194
- // Clean up AI response - remove markdown, explanations, etc.
195
- convertedText = this.cleanAIResponse(convertedText);
196
-
197
- return convertedText;
198
- }
199
-
200
- async convertWithCopilot(text, platform = null) {
201
- if (!this.copilot) {
202
- throw new Error('GitHub Copilot not configured');
203
- }
204
-
205
- const prompt = this.getSystemPrompt(platform).replace('{INPUT}', text);
206
-
207
- const response = await this.copilot.chat.completions.create({
208
- model: 'gpt-4o',
209
- messages: [{ role: 'user', content: prompt }],
210
- temperature: 0.3,
211
- max_tokens: 500,
212
- });
213
-
214
- let convertedText = response.choices[0].message.content.trim();
215
-
216
- // Clean up AI response
217
- convertedText = this.cleanAIResponse(convertedText);
218
-
219
- return convertedText;
220
- }
221
-
222
- // Clean up AI responses - remove markdown, explanations, etc.
223
- cleanAIResponse(text) {
224
- // Remove markdown code blocks
225
- text = text.replace(/```[\s\S]*?```/g, '').trim();
226
- text = text.replace(/```/g, '').trim();
227
-
228
- // Remove any lines that look like explanations (starting with explanatory text)
229
- const lines = text.split('\n');
230
- const cleanedLines = lines.filter(line => {
231
- const trimmed = line.trim();
232
- if (!trimmed) return false;
233
-
234
- // Keep lines that are commands or WAIT
235
- if (trimmed.startsWith('launch ')) return true;
236
- if (trimmed.startsWith('kill ')) return true;
237
- if (trimmed === 'home') return true;
238
- if (trimmed === 'back') return true;
239
- if (trimmed.startsWith('url ')) return true;
240
- if (trimmed.startsWith('click ')) return true;
241
- if (trimmed.startsWith('tap ')) return true;
242
- if (trimmed.startsWith('longpress ')) return true;
243
- if (trimmed.startsWith('swipe ')) return true;
244
- if (trimmed.startsWith('WAIT ')) return true;
245
- if (trimmed.startsWith('screenshot')) return true;
246
- if (trimmed.startsWith('restart')) return true;
247
- if (trimmed.startsWith('rotate ')) return true;
248
- if (trimmed.startsWith('darkmode')) return true;
249
- if (trimmed.startsWith('lightmode')) return true;
250
- if (trimmed.startsWith('airplane ')) return true;
251
- if (trimmed.startsWith('wifi ')) return true;
252
- if (trimmed.startsWith('volume ')) return true;
253
- if (trimmed === 'getLocators') return true;
254
- if (trimmed === 'recent') return true;
255
- if (trimmed === 'notifications') return true;
256
- if (trimmed === 'quicksettings') return true;
257
-
258
- // If it doesn't start with a known command, it might be text to type
259
- // Check if previous line was a command that expects text input
260
- return true; // For now, include it (could be text to type)
261
- });
262
-
263
- return cleanedLines.join('\n').trim();
264
- }
265
-
266
- getAvailableProviders() {
267
- const available = [];
268
-
269
- if (this.gemini) available.push({ id: 'gemini', name: 'Google Gemini', icon: '✨' });
270
- if (this.copilot) available.push({ id: 'copilot', name: 'GitHub Copilot', icon: '🤖' });
271
-
272
- return available;
273
- }
274
-
275
- setProvider(provider) {
276
- const available = this.getAvailableProviders().map(p => p.id);
277
- if (available.includes(provider)) {
278
- this.provider = provider;
279
- return true;
280
- }
281
- return false;
282
- }
283
-
284
- getCurrentProvider() {
285
- return {
286
- id: this.provider,
287
- name: this.getProviderName(this.provider),
288
- available: this.getAvailableProviders(),
289
- };
290
- }
291
-
292
- getProviderName(providerId) {
293
- const names = {
294
- gemini: 'Google Gemini',
295
- copilot: 'GitHub Copilot',
296
- };
297
- return names[providerId] || 'Unknown';
298
- }
299
- }
300
-
301
- module.exports = AIProviderManager;
@@ -1,2 +0,0 @@
1
- @keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideInRight{0%{opacity:0;transform:translateX(30px)}to{opacity:1;transform:translateX(0)}}@keyframes slideInLeft{0%{opacity:0;transform:translateX(-30px)}to{opacity:1;transform:translateX(0)}}@keyframes scaleIn{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.6;transform:scale(1.15)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes bounce{0%,to{transform:translateY(0)}50%{transform:translateY(-10px)}}@keyframes shimmer{0%{background-position:-1000px 0}to{background-position:1000px 0}}@keyframes rotate{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-8px)}}@keyframes glow{0%,to{box-shadow:0 0 5px #1890ff80}50%{box-shadow:0 0 20px #1890ffcc,0 0 30px #1890ff99}}.fade-in{animation:fadeIn .5s ease-out}.slide-in-right{animation:slideInRight .5s ease-out}.slide-in-left{animation:slideInLeft .5s ease-out}.scale-in{animation:scaleIn .4s cubic-bezier(.34,1.56,.64,1)}.pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.ping{animation:ping 2s cubic-bezier(0,0,.2,1) infinite}.bounce{animation:bounce 1s ease-in-out infinite}.float{animation:float 3s ease-in-out infinite}.shimmer{animation:shimmer 2s infinite;background:linear-gradient(90deg,#fff0 0,#ffffff4d 50%,#fff0);background-size:1000px 100%}.rotate{animation:rotate 1s linear}.glow{animation:glow 2s ease-in-out infinite}*{-webkit-tap-highlight-color:transparent}.smooth-transition{transition:all .3s cubic-bezier(.4,0,.2,1)}.smooth-hover:hover{box-shadow:0 10px 25px #00000026;transform:translateY(-2px)}.glass{background:#ffffff1a;border:1px solid #fff3}.glass,.glass-dark{backdrop-filter:blur(10px) saturate(180%);-webkit-backdrop-filter:blur(10px) saturate(180%)}.glass-dark{background:#0003;border:1px solid #ffffff1a}.card-hover{transition:all .3s cubic-bezier(.4,0,.2,1)}.card-hover:hover{box-shadow:0 20px 40px #0003;transform:translateY(-4px) scale(1.02)}.ripple{overflow:hidden;position:relative}.ripple:after{background:#ffffff80;border-radius:50%;content:"";height:0;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:width .6s,height .6s;width:0}.ripple:active:after{height:300px;width:300px}::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#0000000d;border-radius:10px}::-webkit-scrollbar-thumb{background:#0000004d;border-radius:10px;-webkit-transition:all .3s ease;transition:all .3s ease}::-webkit-scrollbar-thumb:hover{background:#00000080}.skeleton{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,#c8c8c84d 25%,#c8c8c880 50%,#c8c8c84d 75%);background-size:200% 100%;border-radius:8px}:focus-visible{border-radius:4px;outline:2px solid #1890ff99;outline-offset:2px}.no-select{-webkit-user-select:none;user-select:none}.gradient-text{-webkit-text-fill-color:#0000;background:linear-gradient(135deg,#1890ff,#9254de);-webkit-background-clip:text;background-clip:text}.shadow-soft{box-shadow:0 2px 8px #00000014,0 4px 16px #00000014}.shadow-medium{box-shadow:0 4px 12px #0000001a,0 8px 24px #0000001a}.shadow-strong{box-shadow:0 8px 24px #00000026,0 16px 48px #00000026}.stagger-item{animation:fadeIn .5s ease-out forwards;opacity:0}.stagger-item:first-child{animation-delay:.05s}.stagger-item:nth-child(2){animation-delay:.1s}.stagger-item:nth-child(3){animation-delay:.15s}.stagger-item:nth-child(4){animation-delay:.2s}.stagger-item:nth-child(5){animation-delay:.25s}.stagger-item:nth-child(6){animation-delay:.3s}.stagger-item:nth-child(7){animation-delay:.35s}.stagger-item:nth-child(8){animation-delay:.4s}.micro-bounce:hover{animation:bounce .5s ease}.micro-rotate:hover{animation:rotate .5s ease}@media (prefers-reduced-motion:reduce){*,:after,:before{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}
2
- /*# sourceMappingURL=main.23bd35c0.css.map*/
@@ -1 +0,0 @@
1
- {"version":3,"file":"static/css/main.23bd35c0.css","mappings":"AAGA,kBACE,GACE,SAAU,CACV,0BACF,CACA,GACE,SAAU,CACV,uBACF,CACF,CAGA,wBACE,GACE,SAAU,CACV,0BACF,CACA,GACE,SAAU,CACV,uBACF,CACF,CAGA,uBACE,GACE,SAAU,CACV,2BACF,CACA,GACE,SAAU,CACV,uBACF,CACF,CAGA,mBACE,GACE,SAAU,CACV,mBACF,CACA,GACE,SAAU,CACV,kBACF,CACF,CAGA,iBACE,MACE,SAAU,CACV,kBACF,CACA,IACE,UAAY,CACZ,qBACF,CACF,CAGA,gBACE,OAEE,SAAU,CADV,kBAEF,CACF,CAGA,kBACE,MACE,uBACF,CACA,IACE,2BACF,CACF,CAGA,mBACE,GACE,6BACF,CACA,GACE,4BACF,CACF,CAGA,kBACE,GACE,sBACF,CACA,GACE,uBACF,CACF,CAGA,iBACE,MACE,uBACF,CACA,IACE,0BACF,CACF,CAGA,gBACE,MACE,4BACF,CACA,IACE,gDACF,CACF,CAGA,SACE,6BACF,CAEA,gBACE,mCACF,CAEA,eACE,kCACF,CAEA,UACE,kDACF,CAEA,OACE,mDACF,CAEA,MACE,iDACF,CAEA,QACE,wCACF,CAEA,OACE,uCACF,CAEA,SAQE,6BAA8B,CAP9B,6DAKC,CACD,2BAEF,CAEA,QACE,0BACF,CAEA,MACE,sCACF,CAGA,EACE,uCACF,CAEA,mBACE,0CACF,CAEA,oBAEE,gCAA2C,CAD3C,0BAEF,CAGA,OACE,oBAAoC,CAGpC,sBACF,CAEA,mBALE,yCAA0C,CAC1C,iDASF,CALA,YACE,gBAA8B,CAG9B,0BACF,CAGA,YACE,0CACF,CAEA,kBAEE,4BAA0C,CAD1C,sCAEF,CAGA,QAEE,eAAgB,CADhB,iBAEF,CAEA,cAQE,oBAAoC,CADpC,iBAAkB,CANlB,UAAW,CAKX,QAAS,CAFT,QAAS,CAFT,iBAAkB,CAClB,OAAQ,CAMR,8BAAgC,CAChC,+BAAmC,CALnC,OAMF,CAEA,qBAEE,YAAa,CADb,WAEF,CAGA,oBAEE,WAAY,CADZ,UAEF,CAEA,0BACE,oBAA+B,CAC/B,kBACF,CAEA,0BACE,oBAA8B,CAC9B,kBAAmB,CACnB,+BAAyB,CAAzB,uBACF,CAEA,gCACE,oBACF,CAGA,UAQE,+BAAgC,CAPhC,2EAKC,CACD,yBAA0B,CAE1B,iBACF,CAGA,eAGE,iBAAkB,CAFlB,2BAA0C,CAC1C,kBAEF,CAGA,WACE,wBAAyB,CAGzB,gBACF,CAGA,eAGE,6BAAoC,CAFpC,kDAA6D,CAC7D,4BAA6B,CAE7B,oBACF,CAGA,aACE,mDACF,CAEA,eACE,oDACF,CAEA,eACE,qDACF,CAGA,cAEE,sCAAwC,CADxC,SAEF,CAEA,0BAA6B,oBAAwB,CACrD,2BAA6B,mBAAuB,CACpD,2BAA6B,oBAAwB,CACrD,2BAA6B,mBAAuB,CACpD,2BAA6B,oBAAwB,CACrD,2BAA6B,mBAAuB,CACpD,2BAA6B,oBAAwB,CACrD,2BAA6B,mBAAuB,CAGpD,oBACE,yBACF,CAEA,oBACE,yBACF,CAGA,uCACE,iBAGE,kCAAqC,CACrC,qCAAuC,CACvC,mCACF,CACF","sources":["animations.css"],"sourcesContent":["/* Enhanced UI Animations and Effects */\n\n/* Smooth Fade In */\n@keyframes fadeIn {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Slide In From Right */\n@keyframes slideInRight {\n from {\n opacity: 0;\n transform: translateX(30px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n/* Slide In From Left */\n@keyframes slideInLeft {\n from {\n opacity: 0;\n transform: translateX(-30px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n/* Scale In */\n@keyframes scaleIn {\n from {\n opacity: 0;\n transform: scale(0.9);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n/* Pulse Effect */\n@keyframes pulse {\n 0%, 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.6;\n transform: scale(1.15);\n }\n}\n\n/* Ping Effect (Ripple) */\n@keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n/* Bounce */\n@keyframes bounce {\n 0%, 100% {\n transform: translateY(0);\n }\n 50% {\n transform: translateY(-10px);\n }\n}\n\n/* Shimmer Loading */\n@keyframes shimmer {\n 0% {\n background-position: -1000px 0;\n }\n 100% {\n background-position: 1000px 0;\n }\n}\n\n/* Rotate */\n@keyframes rotate {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Float */\n@keyframes float {\n 0%, 100% {\n transform: translateY(0px);\n }\n 50% {\n transform: translateY(-8px);\n }\n}\n\n/* Glow */\n@keyframes glow {\n 0%, 100% {\n box-shadow: 0 0 5px rgba(24, 144, 255, 0.5);\n }\n 50% {\n box-shadow: 0 0 20px rgba(24, 144, 255, 0.8), 0 0 30px rgba(24, 144, 255, 0.6);\n }\n}\n\n/* Apply Animations */\n.fade-in {\n animation: fadeIn 0.5s ease-out;\n}\n\n.slide-in-right {\n animation: slideInRight 0.5s ease-out;\n}\n\n.slide-in-left {\n animation: slideInLeft 0.5s ease-out;\n}\n\n.scale-in {\n animation: scaleIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n\n.pulse {\n animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n.ping {\n animation: ping 2s cubic-bezier(0, 0, 0.2, 1) infinite;\n}\n\n.bounce {\n animation: bounce 1s ease-in-out infinite;\n}\n\n.float {\n animation: float 3s ease-in-out infinite;\n}\n\n.shimmer {\n background: linear-gradient(\n to right,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%\n );\n background-size: 1000px 100%;\n animation: shimmer 2s infinite;\n}\n\n.rotate {\n animation: rotate 1s linear;\n}\n\n.glow {\n animation: glow 2s ease-in-out infinite;\n}\n\n/* Smooth Transitions */\n* {\n -webkit-tap-highlight-color: transparent;\n}\n\n.smooth-transition {\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.smooth-hover:hover {\n transform: translateY(-2px);\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n}\n\n/* Glass Morphism */\n.glass {\n background: rgba(255, 255, 255, 0.1);\n backdrop-filter: blur(10px) saturate(180%);\n -webkit-backdrop-filter: blur(10px) saturate(180%);\n border: 1px solid rgba(255, 255, 255, 0.2);\n}\n\n.glass-dark {\n background: rgba(0, 0, 0, 0.2);\n backdrop-filter: blur(10px) saturate(180%);\n -webkit-backdrop-filter: blur(10px) saturate(180%);\n border: 1px solid rgba(255, 255, 255, 0.1);\n}\n\n/* Card Hover Effects */\n.card-hover {\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.card-hover:hover {\n transform: translateY(-4px) scale(1.02);\n box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);\n}\n\n/* Button Ripple Effect */\n.ripple {\n position: relative;\n overflow: hidden;\n}\n\n.ripple::after {\n content: \"\";\n position: absolute;\n top: 50%;\n left: 50%;\n width: 0;\n height: 0;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.5);\n transform: translate(-50%, -50%);\n transition: width 0.6s, height 0.6s;\n}\n\n.ripple:active::after {\n width: 300px;\n height: 300px;\n}\n\n/* Smooth Scrollbar */\n::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n}\n\n::-webkit-scrollbar-track {\n background: rgba(0, 0, 0, 0.05);\n border-radius: 10px;\n}\n\n::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.3);\n border-radius: 10px;\n transition: all 0.3s ease;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.5);\n}\n\n/* Loading Skeleton */\n.skeleton {\n background: linear-gradient(\n 90deg,\n rgba(200, 200, 200, 0.3) 25%,\n rgba(200, 200, 200, 0.5) 50%,\n rgba(200, 200, 200, 0.3) 75%\n );\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n border-radius: 8px;\n}\n\n/* Focus Visible for Accessibility */\n*:focus-visible {\n outline: 2px solid rgba(24, 144, 255, 0.6);\n outline-offset: 2px;\n border-radius: 4px;\n}\n\n/* Prevent Text Selection on Interactive Elements */\n.no-select {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n\n/* Gradient Text */\n.gradient-text {\n background: linear-gradient(135deg, #1890FF 0%, #9254DE 100%);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n}\n\n/* Enhanced Shadow */\n.shadow-soft {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 4px 16px rgba(0, 0, 0, 0.08);\n}\n\n.shadow-medium {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), 0 8px 24px rgba(0, 0, 0, 0.1);\n}\n\n.shadow-strong {\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15), 0 16px 48px rgba(0, 0, 0, 0.15);\n}\n\n/* Stagger Animation for Lists */\n.stagger-item {\n opacity: 0;\n animation: fadeIn 0.5s ease-out forwards;\n}\n\n.stagger-item:nth-child(1) { animation-delay: 0.05s; }\n.stagger-item:nth-child(2) { animation-delay: 0.1s; }\n.stagger-item:nth-child(3) { animation-delay: 0.15s; }\n.stagger-item:nth-child(4) { animation-delay: 0.2s; }\n.stagger-item:nth-child(5) { animation-delay: 0.25s; }\n.stagger-item:nth-child(6) { animation-delay: 0.3s; }\n.stagger-item:nth-child(7) { animation-delay: 0.35s; }\n.stagger-item:nth-child(8) { animation-delay: 0.4s; }\n\n/* Micro-interactions */\n.micro-bounce:hover {\n animation: bounce 0.5s ease;\n}\n\n.micro-rotate:hover {\n animation: rotate 0.5s ease;\n}\n\n/* Accessibility */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n"],"names":[],"ignoreList":[],"sourceRoot":""}