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/README.md +40 -3
- package/bin/devicely.js +1 -1
- package/config/devices.conf +22 -6
- package/lib/androidDeviceDetection.js +1 -1
- package/lib/appMappings.js +1 -1
- package/lib/commanderService.js +1 -0
- package/lib/deviceDetection.js +1 -1
- package/lib/devices.js +1 -1
- package/lib/doctor.js +1 -1
- package/lib/encryption.js +1 -1
- package/lib/executor.js +1 -1
- package/lib/frontend/asset-manifest.json +6 -6
- package/lib/frontend/index.html +1 -1
- package/lib/locatorStrategy.js +1 -0
- package/lib/logger.js +1 -1
- package/lib/package.json +1 -0
- package/lib/public/asset-manifest.json +13 -0
- package/lib/public/index.html +1 -0
- package/lib/public/index.html.bak +1 -0
- package/lib/public/static/css/main.1bbcfef2.css +1 -0
- package/lib/public/static/css/main.ed39ba12.css +2 -0
- package/lib/public/static/css/main.ed39ba12.css.map +1 -0
- package/lib/public/voice-test.html +156 -0
- package/lib/scriptLoader.js +1 -1
- package/lib/server.js +1 -1
- package/package.json +6 -3
- package/lib/aiProviders.js.backup +0 -301
- package/lib/frontend/static/css/main.23bd35c0.css +0 -2
- package/lib/frontend/static/css/main.23bd35c0.css.map +0 -1
- package/lib/frontend/static/js/main.81eae090.js +0 -3
- package/lib/frontend/static/js/main.81eae090.js.LICENSE.txt +0 -48
- package/lib/frontend/static/js/main.81eae090.js.map +0 -1
- package/lib/package-lock.json +0 -1678
- package/lib/server.js.bak +0 -3546
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devicely",
|
|
3
|
-
"version": "2.2.
|
|
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":""}
|