vg-coder-cli 2.0.8 ā 2.0.10
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 +19 -0
- package/SYSTEM_PROMPT.md +157 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +206 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +206 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/detectors/index.html +116 -0
- package/coverage/lcov-report/src/detectors/project-detector.js.html +1084 -0
- package/coverage/lcov-report/src/exporter/html-exporter.js.html +2839 -0
- package/coverage/lcov-report/src/exporter/index.html +116 -0
- package/coverage/lcov-report/src/ignore/ignore-manager.js.html +979 -0
- package/coverage/lcov-report/src/ignore/index.html +116 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/index.js.html +928 -0
- package/coverage/lcov-report/src/scanner/file-scanner.js.html +1903 -0
- package/coverage/lcov-report/src/scanner/index.html +116 -0
- package/coverage/lcov-report/src/tokenizer/index.html +116 -0
- package/coverage/lcov-report/src/tokenizer/token-manager.js.html +1252 -0
- package/coverage/lcov-report/src/utils/helpers.js.html +469 -0
- package/coverage/lcov-report/src/utils/index.html +116 -0
- package/coverage/lcov.info +1396 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/detectors/index.html +116 -0
- package/coverage/src/detectors/project-detector.js.html +1084 -0
- package/coverage/src/exporter/html-exporter.js.html +2839 -0
- package/coverage/src/exporter/index.html +116 -0
- package/coverage/src/ignore/ignore-manager.js.html +979 -0
- package/coverage/src/ignore/index.html +116 -0
- package/coverage/src/index.html +116 -0
- package/coverage/src/index.js.html +928 -0
- package/coverage/src/scanner/file-scanner.js.html +1903 -0
- package/coverage/src/scanner/index.html +116 -0
- package/coverage/src/tokenizer/index.html +116 -0
- package/coverage/src/tokenizer/token-manager.js.html +1252 -0
- package/coverage/src/utils/helpers.js.html +469 -0
- package/coverage/src/utils/index.html +116 -0
- package/jest.config.js +16 -0
- package/package.json +5 -3
- package/scripts/build.js +40 -0
- package/src/scanner/file-scanner.js +3 -104
- package/src/server/api-server.js +74 -18
- package/src/server/views/css/structure.css +148 -0
- package/src/server/views/dashboard.css +176 -312
- package/src/server/views/dashboard.html +153 -85
- package/src/server/views/js/api.js +19 -2
- package/src/server/views/js/features/structure.js +221 -0
- package/src/server/views/js/handlers.js +38 -70
- package/src/server/views/js/main.js +60 -0
- package/src/server/views/js/utils.js +10 -0
- package/src/server/views/vg-coder/assets/icon128.png +0 -0
- package/src/server/views/vg-coder/assets/icon16.png +0 -0
- package/src/server/views/vg-coder/assets/icon48.png +0 -0
- package/src/server/views/vg-coder/background.js +2 -0
- package/src/server/views/vg-coder/background.js.LICENSE.txt +118 -0
- package/src/server/views/vg-coder/controller.js +1 -0
- package/src/server/views/vg-coder/manifest.json +58 -0
- package/src/server/views/vg-coder/options.css +164 -0
- package/src/server/views/vg-coder/options.html +48 -0
- package/src/server/views/vg-coder/options.js +1 -0
- package/src/server/views/vg-coder/rules.json +23 -0
- package/src/tokenizer/token-manager.js +52 -2
- package/vg-coder-cli-2.0.10.tgz +0 -0
- package/vg-coder.zip +0 -0
- package/vg-coder-cli-2.0.8.tgz +0 -0
package/scripts/build.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const AdmZip = require('adm-zip');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
|
|
5
|
+
async function build() {
|
|
6
|
+
try {
|
|
7
|
+
const rootDir = path.resolve(__dirname, '..');
|
|
8
|
+
const zipPath = path.join(rootDir, 'vg-coder.zip');
|
|
9
|
+
const targetDir = path.join(rootDir, 'src', 'server', 'views', 'vg-coder');
|
|
10
|
+
|
|
11
|
+
console.log('šļø Starting build process...');
|
|
12
|
+
|
|
13
|
+
// 1. Check if zip exists
|
|
14
|
+
if (!fs.existsSync(zipPath)) {
|
|
15
|
+
console.error('ā ļø vg-coder.zip not found at root. Skipping extension extraction.');
|
|
16
|
+
// We don't exit 1 here to allow build to continue if zip is missing in dev
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 2. Clean old directory
|
|
21
|
+
if (fs.existsSync(targetDir)) {
|
|
22
|
+
console.log('š§¹ Cleaning old extension directory...');
|
|
23
|
+
fs.removeSync(targetDir);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 3. Unzip
|
|
27
|
+
console.log('š¦ Unzipping vg-coder.zip...');
|
|
28
|
+
const zip = new AdmZip(zipPath);
|
|
29
|
+
zip.extractAllTo(targetDir, true);
|
|
30
|
+
|
|
31
|
+
console.log(`ā
Extension extracted to: ${targetDir}`);
|
|
32
|
+
console.log('š Build completed successfully!');
|
|
33
|
+
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error('ā Build failed:', error);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
build();
|
|
@@ -106,6 +106,7 @@ class FileScanner {
|
|
|
106
106
|
|
|
107
107
|
const node = {
|
|
108
108
|
path: dirPath,
|
|
109
|
+
relativePath: relativePath,
|
|
109
110
|
name: name,
|
|
110
111
|
size: stats.size,
|
|
111
112
|
type: stats.isDirectory() ? 'directory' : 'file',
|
|
@@ -148,47 +149,6 @@ class FileScanner {
|
|
|
148
149
|
return node;
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
/**
|
|
152
|
-
* Get regex for build directories and hidden files to ignore
|
|
153
|
-
*/
|
|
154
|
-
getBuildIgnoreRegex() {
|
|
155
|
-
// Create regex pattern for directories to ignore
|
|
156
|
-
const ignorePatterns = [
|
|
157
|
-
// Hidden directories (starting with .)
|
|
158
|
-
'^\\..*',
|
|
159
|
-
|
|
160
|
-
// Build directories
|
|
161
|
-
'^build$',
|
|
162
|
-
'^target$',
|
|
163
|
-
'^dist$',
|
|
164
|
-
'^out$',
|
|
165
|
-
'^bin$',
|
|
166
|
-
|
|
167
|
-
// Dependencies
|
|
168
|
-
'^node_modules$',
|
|
169
|
-
'^vendor$',
|
|
170
|
-
|
|
171
|
-
// Temporary files
|
|
172
|
-
'^tmp$',
|
|
173
|
-
'^temp$',
|
|
174
|
-
|
|
175
|
-
// Logs
|
|
176
|
-
'^logs$',
|
|
177
|
-
'^log$',
|
|
178
|
-
|
|
179
|
-
// Coverage reports
|
|
180
|
-
'^coverage$'
|
|
181
|
-
];
|
|
182
|
-
|
|
183
|
-
// Combine all patterns into one regex
|
|
184
|
-
const combinedPattern = ignorePatterns.join('|');
|
|
185
|
-
return new RegExp(`(${combinedPattern})`);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
152
|
/**
|
|
193
153
|
* TrĆch xuįŗ„t danh sĆ”ch files từ tree
|
|
194
154
|
*/
|
|
@@ -381,69 +341,8 @@ class FileScanner {
|
|
|
381
341
|
* Tįŗ”o nį»i dung kįŗæt hợp từ tįŗ„t cįŗ£ files
|
|
382
342
|
*/
|
|
383
343
|
async createCombinedContent(files, options = {}) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
includeTree = true,
|
|
387
|
-
headerTemplate = this.getDefaultHeaderTemplate(),
|
|
388
|
-
separatorTemplate = this.getDefaultSeparatorTemplate()
|
|
389
|
-
} = options;
|
|
390
|
-
|
|
391
|
-
let content = '';
|
|
392
|
-
|
|
393
|
-
// Header vį»i thĆ“ng tin project
|
|
394
|
-
if (includeStats) {
|
|
395
|
-
content += this.generateProjectHeader(files);
|
|
396
|
-
content += '\n\n';
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// CẄu trúc thư mỄc
|
|
400
|
-
if (includeTree) {
|
|
401
|
-
content += this.generateTreeStructure(files);
|
|
402
|
-
content += '\n\n';
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Nį»i dung từng file
|
|
406
|
-
for (let i = 0; i < files.length; i++) {
|
|
407
|
-
const file = files[i];
|
|
408
|
-
|
|
409
|
-
// Header cho file
|
|
410
|
-
content += headerTemplate
|
|
411
|
-
.replace('{path}', file.relativePath)
|
|
412
|
-
.replace('{name}', file.name)
|
|
413
|
-
.replace('{extension}', file.extension || '')
|
|
414
|
-
.replace('{size}', file.size)
|
|
415
|
-
.replace('{lines}', file.lines);
|
|
416
|
-
|
|
417
|
-
content += '\n';
|
|
418
|
-
content += file.content;
|
|
419
|
-
content += '\n';
|
|
420
|
-
|
|
421
|
-
// Separator giữa cÔc files
|
|
422
|
-
if (i < files.length - 1) {
|
|
423
|
-
content += separatorTemplate;
|
|
424
|
-
content += '\n';
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
return content;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Template header mįŗ·c Äį»nh cho file
|
|
433
|
-
*/
|
|
434
|
-
getDefaultHeaderTemplate() {
|
|
435
|
-
return `
|
|
436
|
-
================================================================================
|
|
437
|
-
File: {path}
|
|
438
|
-
Size: {size} bytes | Lines: {lines}
|
|
439
|
-
================================================================================`;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Template separator mįŗ·c Äį»nh
|
|
444
|
-
*/
|
|
445
|
-
getDefaultSeparatorTemplate() {
|
|
446
|
-
return '\n\n';
|
|
344
|
+
// ... (unchanged)
|
|
345
|
+
return this.createCombinedContentForAI(files, options);
|
|
447
346
|
}
|
|
448
347
|
|
|
449
348
|
/**
|
package/src/server/api-server.js
CHANGED
|
@@ -29,8 +29,8 @@ class ApiServer {
|
|
|
29
29
|
*/
|
|
30
30
|
setupMiddleware() {
|
|
31
31
|
this.app.use(cors());
|
|
32
|
-
this.app.use(bodyParser.json());
|
|
33
|
-
this.app.use(bodyParser.urlencoded({ extended: true }));
|
|
32
|
+
this.app.use(bodyParser.json({ limit: '50mb' })); // Increase limit for large file lists
|
|
33
|
+
this.app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
|
|
34
34
|
|
|
35
35
|
// Serve static files from views directory (CSS, JS)
|
|
36
36
|
this.app.use(express.static(path.join(__dirname, 'views')));
|
|
@@ -60,10 +60,25 @@ class ApiServer {
|
|
|
60
60
|
});
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
// NEW: Get Extension Path
|
|
64
|
+
this.app.get('/api/extension-path', (req, res) => {
|
|
65
|
+
try {
|
|
66
|
+
const extensionPath = path.join(__dirname, 'views', 'vg-coder');
|
|
67
|
+
const exists = fs.existsSync(extensionPath);
|
|
68
|
+
|
|
69
|
+
res.json({
|
|
70
|
+
path: extensionPath,
|
|
71
|
+
exists: exists
|
|
72
|
+
});
|
|
73
|
+
} catch (error) {
|
|
74
|
+
res.status(500).json({ error: error.message });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
63
78
|
// Analyze endpoint - returns project.txt file
|
|
64
79
|
this.app.post('/api/analyze', async (req, res) => {
|
|
65
80
|
try {
|
|
66
|
-
const { path: projectPath, options = {} } = req.body;
|
|
81
|
+
const { path: projectPath, options = {}, specificFiles } = req.body;
|
|
67
82
|
|
|
68
83
|
if (!projectPath) {
|
|
69
84
|
return res.status(400).json({
|
|
@@ -81,6 +96,9 @@ class ApiServer {
|
|
|
81
96
|
}
|
|
82
97
|
|
|
83
98
|
console.log(chalk.yellow(`Analyzing project: ${resolvedPath}`));
|
|
99
|
+
if (specificFiles) {
|
|
100
|
+
console.log(chalk.yellow(`Filtering for ${specificFiles.length} specific files`));
|
|
101
|
+
}
|
|
84
102
|
|
|
85
103
|
// Detect project type
|
|
86
104
|
const detector = new ProjectDetector(resolvedPath);
|
|
@@ -95,8 +113,15 @@ class ApiServer {
|
|
|
95
113
|
const scanner = new FileScanner(resolvedPath, scannerOptions);
|
|
96
114
|
const scanResult = await scanner.scanProject();
|
|
97
115
|
|
|
116
|
+
let filesToProcess = scanResult.files;
|
|
117
|
+
|
|
118
|
+
// Filter specific files if requested
|
|
119
|
+
if (specificFiles && Array.isArray(specificFiles) && specificFiles.length > 0) {
|
|
120
|
+
filesToProcess = filesToProcess.filter(file => specificFiles.includes(file.relativePath));
|
|
121
|
+
}
|
|
122
|
+
|
|
98
123
|
// Create AI-friendly content
|
|
99
|
-
const aiContent = await scanner.createCombinedContentForAI(
|
|
124
|
+
const aiContent = await scanner.createCombinedContentForAI(filesToProcess, {
|
|
100
125
|
includeStats: true,
|
|
101
126
|
includeTree: true,
|
|
102
127
|
preserveLineNumbers: true
|
|
@@ -107,7 +132,7 @@ class ApiServer {
|
|
|
107
132
|
res.setHeader('Content-Disposition', 'attachment; filename="project.txt"');
|
|
108
133
|
res.send(aiContent);
|
|
109
134
|
|
|
110
|
-
console.log(chalk.green(`ā Analysis completed: ${
|
|
135
|
+
console.log(chalk.green(`ā Analysis completed: ${filesToProcess.length} files`));
|
|
111
136
|
|
|
112
137
|
} catch (error) {
|
|
113
138
|
console.error(chalk.red('Error during analysis:'), error);
|
|
@@ -181,6 +206,50 @@ class ApiServer {
|
|
|
181
206
|
}
|
|
182
207
|
});
|
|
183
208
|
|
|
209
|
+
// Structure endpoint
|
|
210
|
+
this.app.get('/api/structure', async (req, res) => {
|
|
211
|
+
try {
|
|
212
|
+
const projectPath = req.query.path || '.';
|
|
213
|
+
const resolvedPath = path.resolve(projectPath);
|
|
214
|
+
|
|
215
|
+
// Validate path
|
|
216
|
+
if (!await fs.pathExists(resolvedPath)) {
|
|
217
|
+
return res.status(404).json({
|
|
218
|
+
error: `Project path does not exist: ${projectPath}`
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
console.log(chalk.yellow(`Analyzing structure for: ${resolvedPath}`));
|
|
223
|
+
|
|
224
|
+
// 1. Scan files
|
|
225
|
+
const scanner = new FileScanner(resolvedPath);
|
|
226
|
+
const scanResult = await scanner.scanProject();
|
|
227
|
+
|
|
228
|
+
// 2. Tokenize tree
|
|
229
|
+
const tokenManager = new TokenManager();
|
|
230
|
+
const enrichedTree = tokenManager.analyzeTree(scanResult.tree, scanResult.files);
|
|
231
|
+
|
|
232
|
+
tokenManager.cleanup();
|
|
233
|
+
|
|
234
|
+
// 3. Return result
|
|
235
|
+
res.json({
|
|
236
|
+
path: resolvedPath,
|
|
237
|
+
totalFiles: scanResult.files.length,
|
|
238
|
+
rootTokens: enrichedTree.tokens,
|
|
239
|
+
structure: enrichedTree
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
console.log(chalk.green(`ā Structure analysis completed: ${scanResult.files.length} files`));
|
|
243
|
+
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(chalk.red('Error getting structure:'), error);
|
|
246
|
+
res.status(500).json({
|
|
247
|
+
error: 'Failed to get structure',
|
|
248
|
+
message: error.message
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
184
253
|
// Clean endpoint
|
|
185
254
|
this.app.delete('/api/clean', async (req, res) => {
|
|
186
255
|
try {
|
|
@@ -285,23 +354,10 @@ class ApiServer {
|
|
|
285
354
|
console.log(chalk.green(`\nš VG Coder API Server started!`));
|
|
286
355
|
console.log(chalk.blue(`š” Listening on: http://localhost:${this.port}`));
|
|
287
356
|
console.log(chalk.cyan(`\nšØ Dashboard: http://localhost:${this.port}`));
|
|
288
|
-
console.log(chalk.yellow(`\nš Available endpoints:`));
|
|
289
|
-
console.log(` GET /health - Health check`);
|
|
290
|
-
console.log(` POST /api/analyze - Analyze project (returns project.txt)`);
|
|
291
|
-
console.log(` GET /api/info?path=. - Get project info`);
|
|
292
|
-
console.log(` DELETE /api/clean - Clean output directory`);
|
|
293
|
-
console.log(` POST /api/execute - Execute bash script`);
|
|
294
|
-
console.log(chalk.gray(`\nš” Press Ctrl+C to stop the server\n`));
|
|
295
357
|
resolve();
|
|
296
358
|
});
|
|
297
359
|
|
|
298
360
|
this.server.on('error', (err) => {
|
|
299
|
-
if (err.code === 'EADDRINUSE') {
|
|
300
|
-
console.error(chalk.red(`\nā Port ${this.port} is already in use!`));
|
|
301
|
-
console.log(chalk.yellow(`Try using a different port with: vg start -p <port>\n`));
|
|
302
|
-
} else {
|
|
303
|
-
console.error(chalk.red('\nā Server error:'), err.message);
|
|
304
|
-
}
|
|
305
361
|
reject(err);
|
|
306
362
|
});
|
|
307
363
|
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/* Tree View Styles Compact */
|
|
2
|
+
.tree-container {
|
|
3
|
+
margin-top: 15px;
|
|
4
|
+
background: var(--ios-input-bg);
|
|
5
|
+
border-radius: 8px;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.tree-header {
|
|
11
|
+
padding: 8px 12px;
|
|
12
|
+
background: rgba(0, 0, 0, 0.03);
|
|
13
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
|
14
|
+
font-weight: 600;
|
|
15
|
+
display: flex;
|
|
16
|
+
justify-content: space-between;
|
|
17
|
+
align-items: center;
|
|
18
|
+
color: var(--text-primary);
|
|
19
|
+
font-size: 12px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.tree-content {
|
|
23
|
+
padding: 5px;
|
|
24
|
+
font-family: 'SF Mono', 'Menlo', monospace;
|
|
25
|
+
font-size: 11px;
|
|
26
|
+
/* Smaller tree font */
|
|
27
|
+
overflow-x: auto;
|
|
28
|
+
max-height: 400px;
|
|
29
|
+
overflow-y: auto;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.tree-ul {
|
|
33
|
+
list-style: none;
|
|
34
|
+
padding-left: 16px;
|
|
35
|
+
/* Reduced indentation */
|
|
36
|
+
margin: 0;
|
|
37
|
+
border-left: 1px solid var(--ios-separator);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Root level shouldn't have border */
|
|
41
|
+
.tree-content>.tree-ul {
|
|
42
|
+
border-left: none;
|
|
43
|
+
padding-left: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.tree-li {
|
|
47
|
+
margin: 1px 0;
|
|
48
|
+
/* Compact spacing */
|
|
49
|
+
position: relative;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.tree-item-row {
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
padding: 2px 6px;
|
|
56
|
+
/* Compact row padding */
|
|
57
|
+
border-radius: 4px;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
transition: background 0.1s;
|
|
60
|
+
line-height: 1.2;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.tree-item-row:hover {
|
|
64
|
+
background: rgba(0, 0, 0, 0.05);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Checkbox Style */
|
|
68
|
+
.tree-checkbox {
|
|
69
|
+
margin-right: 6px;
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
width: 14px;
|
|
72
|
+
/* Smaller checkbox */
|
|
73
|
+
height: 14px;
|
|
74
|
+
accent-color: var(--ios-blue);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.tree-icon {
|
|
78
|
+
margin-right: 6px;
|
|
79
|
+
font-size: 12px;
|
|
80
|
+
width: 14px;
|
|
81
|
+
text-align: center;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.tree-name {
|
|
85
|
+
flex: 1;
|
|
86
|
+
white-space: nowrap;
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
text-overflow: ellipsis;
|
|
89
|
+
margin-right: 8px;
|
|
90
|
+
color: var(--text-primary);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Token Badges */
|
|
94
|
+
.token-badge {
|
|
95
|
+
font-size: 9px;
|
|
96
|
+
/* Very small badge */
|
|
97
|
+
padding: 1px 4px;
|
|
98
|
+
border-radius: 3px;
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
min-width: 30px;
|
|
101
|
+
text-align: center;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.token-low {
|
|
105
|
+
background: rgba(52, 199, 89, 0.15);
|
|
106
|
+
color: var(--ios-green);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* < 2k */
|
|
110
|
+
.token-med {
|
|
111
|
+
background: rgba(255, 149, 0, 0.15);
|
|
112
|
+
color: #FF9500;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* 2k - 5k */
|
|
116
|
+
.token-high {
|
|
117
|
+
background: rgba(255, 59, 48, 0.15);
|
|
118
|
+
color: var(--ios-red);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* > 5k */
|
|
122
|
+
|
|
123
|
+
/* Folder toggle states */
|
|
124
|
+
.tree-li>.tree-ul {
|
|
125
|
+
display: block;
|
|
126
|
+
/* Default expanded */
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.tree-li.collapsed>.tree-ul {
|
|
130
|
+
display: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.arrow {
|
|
134
|
+
display: inline-block;
|
|
135
|
+
width: 10px;
|
|
136
|
+
font-size: 8px;
|
|
137
|
+
color: var(--text-secondary);
|
|
138
|
+
transition: transform 0.2s;
|
|
139
|
+
margin-right: 2px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.tree-li.collapsed>.tree-item-row .arrow {
|
|
143
|
+
transform: rotate(-90deg);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.tree-li:not(.has-children) .arrow {
|
|
147
|
+
visibility: hidden;
|
|
148
|
+
}
|