awacke1 commited on
Commit
8196ee6
Β·
verified Β·
1 Parent(s): 875e204

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +361 -2
index.html CHANGED
@@ -236,7 +236,92 @@
236
  .legend-color.document { background: linear-gradient(135deg, #fdcb6e 0%, #e17055 100%); }
237
  .legend-color.office { background: linear-gradient(135deg, #00b894 0%, #00a085 100%); }
238
  .legend-color.code { background: linear-gradient(135deg, #81ecec 0%, #00cec9 100%); }
239
- .legend-color.other { background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
  .tooltip {
242
  position: absolute;
@@ -381,19 +466,63 @@
381
  <div class="visualization-area" id="visualizationArea">
382
  <div style="text-align: center; padding: 60px; color: #999;">
383
  <h3>🎯 Ready to Explore</h3>
384
- <p>Select a folder above to visualize its structure, or try the demo data to see how it works!</p>
 
385
  </div>
386
  </div>
387
  </div>
388
 
389
  <div class="tooltip" id="tooltip"></div>
390
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  <script>
392
  class SquarifiedTreemapExplorer {
393
  constructor() {
394
  this.tooltip = document.getElementById('tooltip');
395
  this.visualizationArea = document.getElementById('visualizationArea');
396
  this.folderInput = document.getElementById('folderInput');
 
 
 
397
  this.fileData = null;
398
  this.setupEventListeners();
399
  }
@@ -404,6 +533,20 @@
404
  this.tooltip.style.top = e.pageY + 10 + 'px';
405
  });
406
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  // Listen for file input changes
408
  this.folderInput.addEventListener('change', (e) => {
409
  if (e.target.files.length > 0) {
@@ -914,6 +1057,222 @@
914
  element.addEventListener('mouseleave', () => {
915
  this.tooltip.classList.remove('visible');
916
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
917
  }
918
 
919
  createTooltipContent(data) {
 
236
  .legend-color.document { background: linear-gradient(135deg, #fdcb6e 0%, #e17055 100%); }
237
  .legend-color.office { background: linear-gradient(135deg, #00b894 0%, #00a085 100%); }
238
  .legend-color.code { background: linear-gradient(135deg, #81ecec 0%, #00cec9 100%); }
239
+ .treemap-node.selected {
240
+ border: 3px solid #667eea !important;
241
+ z-index: 99;
242
+ }
243
+
244
+ .context-menu {
245
+ position: fixed;
246
+ background: white;
247
+ border-radius: 8px;
248
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
249
+ padding: 8px 0;
250
+ z-index: 10000;
251
+ min-width: 200px;
252
+ border: 1px solid #e9ecef;
253
+ display: none;
254
+ }
255
+
256
+ .context-menu-item {
257
+ padding: 10px 16px;
258
+ cursor: pointer;
259
+ transition: background-color 0.2s;
260
+ display: flex;
261
+ align-items: center;
262
+ gap: 10px;
263
+ font-size: 14px;
264
+ }
265
+
266
+ .context-menu-item:hover {
267
+ background-color: #f8f9fa;
268
+ }
269
+
270
+ .context-menu-item.disabled {
271
+ opacity: 0.5;
272
+ cursor: not-allowed;
273
+ }
274
+
275
+ .context-menu-item.disabled:hover {
276
+ background-color: white;
277
+ }
278
+
279
+ .context-menu-separator {
280
+ height: 1px;
281
+ background-color: #e9ecef;
282
+ margin: 4px 0;
283
+ }
284
+
285
+ .file-operations-panel {
286
+ position: fixed;
287
+ top: 20px;
288
+ right: 20px;
289
+ background: white;
290
+ border-radius: 10px;
291
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
292
+ padding: 20px;
293
+ max-width: 300px;
294
+ z-index: 1000;
295
+ display: none;
296
+ }
297
+
298
+ .operations-header {
299
+ font-weight: bold;
300
+ margin-bottom: 10px;
301
+ color: #667eea;
302
+ }
303
+
304
+ .space-savings {
305
+ background: #e8f5e8;
306
+ padding: 10px;
307
+ border-radius: 5px;
308
+ margin-top: 10px;
309
+ border-left: 3px solid #28a745;
310
+ }
311
+
312
+ .warning-note {
313
+ background: #fff3cd;
314
+ border: 1px solid #ffeaa7;
315
+ border-radius: 5px;
316
+ padding: 15px;
317
+ margin: 20px;
318
+ color: #856404;
319
+ }
320
+
321
+ .warning-note h4 {
322
+ margin: 0 0 10px 0;
323
+ color: #856404;
324
+ }
325
 
326
  .tooltip {
327
  position: absolute;
 
466
  <div class="visualization-area" id="visualizationArea">
467
  <div style="text-align: center; padding: 60px; color: #999;">
468
  <h3>🎯 Ready to Explore</h3>
469
+ <p>Select a folder above to visualize its structure, or try the demo data!</p>
470
+ <p style="font-size: 0.9em; margin-top: 10px;">πŸ’‘ <strong>Tip:</strong> Right-click any file or folder for operations menu</p>
471
  </div>
472
  </div>
473
  </div>
474
 
475
  <div class="tooltip" id="tooltip"></div>
476
 
477
+ <div class="context-menu" id="contextMenu">
478
+ <div class="context-menu-item" onclick="app.copyPath()">
479
+ πŸ“‹ Copy Path
480
+ </div>
481
+ <div class="context-menu-item" onclick="app.showFileInfo()">
482
+ ℹ️ File Info
483
+ </div>
484
+ <div class="context-menu-separator"></div>
485
+ <div class="context-menu-item disabled" onclick="app.showDeleteSimulation()">
486
+ πŸ—‘οΈ Simulate Delete
487
+ </div>
488
+ <div class="context-menu-item disabled">
489
+ βœ‚οΈ Move File (Browser Limited)
490
+ </div>
491
+ <div class="context-menu-separator"></div>
492
+ <div class="context-menu-item" onclick="app.selectSimilarFiles()">
493
+ πŸ” Select Similar Files
494
+ </div>
495
+ </div>
496
+
497
+ <div class="file-operations-panel" id="operationsPanel">
498
+ <div class="operations-header">File Operations</div>
499
+ <div id="operationContent">
500
+ Select a file or folder to see available operations
501
+ </div>
502
+ <button onclick="app.closeOperationsPanel()" style="margin-top: 10px; padding: 5px 10px; border: none; background: #667eea; color: white; border-radius: 3px; cursor: pointer;">Close</button>
503
+ </div>
504
+
505
+ <div class="warning-note">
506
+ <h4>⚠️ Browser Security Limitations</h4>
507
+ <p>Web browsers cannot directly modify files on your computer for security reasons. This tool provides:</p>
508
+ <ul>
509
+ <li>βœ… Local file analysis (no uploads)</li>
510
+ <li>βœ… Visual space usage mapping</li>
511
+ <li>βœ… File information and path copying</li>
512
+ <li>❌ Cannot delete/move files directly</li>
513
+ </ul>
514
+ <p><strong>For actual file operations:</strong> Use your system's file manager with the paths this tool provides.</p>
515
+ </div>
516
+
517
  <script>
518
  class SquarifiedTreemapExplorer {
519
  constructor() {
520
  this.tooltip = document.getElementById('tooltip');
521
  this.visualizationArea = document.getElementById('visualizationArea');
522
  this.folderInput = document.getElementById('folderInput');
523
+ this.contextMenu = document.getElementById('contextMenu');
524
+ this.operationsPanel = document.getElementById('operationsPanel');
525
+ this.selectedFileData = null;
526
  this.fileData = null;
527
  this.setupEventListeners();
528
  }
 
533
  this.tooltip.style.top = e.pageY + 10 + 'px';
534
  });
535
 
536
+ // Hide context menu on click elsewhere
537
+ document.addEventListener('click', (e) => {
538
+ if (!this.contextMenu.contains(e.target)) {
539
+ this.contextMenu.style.display = 'none';
540
+ }
541
+ });
542
+
543
+ // Prevent default context menu
544
+ document.addEventListener('contextmenu', (e) => {
545
+ if (e.target.classList.contains('treemap-node')) {
546
+ e.preventDefault();
547
+ }
548
+ });
549
+
550
  // Listen for file input changes
551
  this.folderInput.addEventListener('change', (e) => {
552
  if (e.target.files.length > 0) {
 
1057
  element.addEventListener('mouseleave', () => {
1058
  this.tooltip.classList.remove('visible');
1059
  });
1060
+
1061
+ // Add right-click context menu
1062
+ element.addEventListener('contextmenu', (e) => {
1063
+ e.preventDefault();
1064
+ this.selectedFileData = data;
1065
+ this.showContextMenu(e.pageX, e.pageY);
1066
+ });
1067
+
1068
+ // Add click handler for selection
1069
+ element.addEventListener('click', (e) => {
1070
+ e.stopPropagation();
1071
+ this.selectedFileData = data;
1072
+ this.highlightSelection(element);
1073
+ });
1074
+ }
1075
+
1076
+ showContextMenu(x, y) {
1077
+ this.contextMenu.style.left = x + 'px';
1078
+ this.contextMenu.style.top = y + 'px';
1079
+ this.contextMenu.style.display = 'block';
1080
+
1081
+ // Enable/disable menu items based on selection
1082
+ const deleteItem = this.contextMenu.querySelector('.context-menu-item.disabled');
1083
+ if (this.selectedFileData) {
1084
+ deleteItem.classList.remove('disabled');
1085
+ deleteItem.onclick = () => this.showDeleteSimulation();
1086
+ }
1087
+ }
1088
+
1089
+ highlightSelection(element) {
1090
+ // Remove previous selection
1091
+ document.querySelectorAll('.treemap-node.selected').forEach(el => {
1092
+ el.classList.remove('selected');
1093
+ el.style.border = '1px solid #fff';
1094
+ });
1095
+
1096
+ // Highlight selected element
1097
+ element.classList.add('selected');
1098
+ element.style.border = '3px solid #667eea';
1099
+ }
1100
+
1101
+ copyPath() {
1102
+ if (!this.selectedFileData) return;
1103
+
1104
+ navigator.clipboard.writeText(this.selectedFileData.path).then(() => {
1105
+ this.showNotification(`πŸ“‹ Copied: ${this.selectedFileData.path}`);
1106
+ }).catch(() => {
1107
+ // Fallback for older browsers
1108
+ const textArea = document.createElement('textarea');
1109
+ textArea.value = this.selectedFileData.path;
1110
+ document.body.appendChild(textArea);
1111
+ textArea.select();
1112
+ document.execCommand('copy');
1113
+ document.body.removeChild(textArea);
1114
+ this.showNotification(`πŸ“‹ Copied: ${this.selectedFileData.path}`);
1115
+ });
1116
+ this.contextMenu.style.display = 'none';
1117
+ }
1118
+
1119
+ showFileInfo() {
1120
+ if (!this.selectedFileData) return;
1121
+
1122
+ const data = this.selectedFileData;
1123
+ let content = `<div class="operations-header">πŸ“„ File Information</div>`;
1124
+ content += `<strong>Name:</strong> ${data.name}<br>`;
1125
+ content += `<strong>Type:</strong> ${data.type}`;
1126
+
1127
+ if (data.type === 'file') {
1128
+ const fileType = this.getFileTypeCategory(data.name);
1129
+ const typeLabels = {
1130
+ image: 'πŸ–ΌοΈ Image',
1131
+ video: 'πŸŽ₯ Video',
1132
+ audio: '🎡 Audio',
1133
+ document: 'πŸ“„ Document',
1134
+ office: 'πŸ“Š Office',
1135
+ code: 'πŸ’» Code',
1136
+ other: 'πŸ“¦ Other'
1137
+ };
1138
+ content += ` (${typeLabels[fileType]})`;
1139
+ }
1140
+
1141
+ content += `<br><strong>Size:</strong> ${this.formatFileSize(data.size)}<br>`;
1142
+ content += `<strong>Path:</strong> <code style="background: #f8f9fa; padding: 2px 4px; border-radius: 3px; font-size: 12px; word-break: break-all;">${data.path}</code><br>`;
1143
+
1144
+ if (data.type === 'file' && data.lastModified) {
1145
+ content += `<strong>Modified:</strong> ${new Date(data.lastModified).toLocaleString()}<br>`;
1146
+ }
1147
+
1148
+ if (data.children) {
1149
+ content += `<strong>Items:</strong> ${data.children.length}<br>`;
1150
+ content += `<strong>Subdirectories:</strong> ${data.children.filter(c => c.type === 'directory').length}<br>`;
1151
+ content += `<strong>Files:</strong> ${data.children.filter(c => c.type === 'file').length}`;
1152
+ }
1153
+
1154
+ document.getElementById('operationContent').innerHTML = content;
1155
+ this.operationsPanel.style.display = 'block';
1156
+ this.contextMenu.style.display = 'none';
1157
+ }
1158
+
1159
+ showDeleteSimulation() {
1160
+ if (!this.selectedFileData) return;
1161
+
1162
+ const data = this.selectedFileData;
1163
+ let content = `<div class="operations-header">πŸ—‘οΈ Delete Simulation</div>`;
1164
+ content += `<p><strong>Would delete:</strong> ${data.name}</p>`;
1165
+ content += `<div class="space-savings">`;
1166
+ content += `πŸ’Ύ <strong>Space to be freed:</strong> ${this.formatFileSize(data.size)}<br>`;
1167
+
1168
+ if (data.type === 'directory' && data.children) {
1169
+ const fileCount = this.countFiles(data);
1170
+ content += `πŸ“ <strong>Items to be removed:</strong> ${fileCount.files} files, ${fileCount.folders} folders`;
1171
+ }
1172
+ content += `</div>`;
1173
+
1174
+ content += `<p style="margin-top: 10px; font-size: 12px; color: #666;">`;
1175
+ content += `<strong>To actually delete:</strong><br>`;
1176
+ content += `1. Copy path: <code style="background: #f8f9fa; padding: 2px 4px; border-radius: 3px; font-size: 11px;">${data.path}</code><br>`;
1177
+ content += `2. Use your system's file manager<br>`;
1178
+ content += `3. Navigate to the copied path<br>`;
1179
+ content += `4. Delete the file/folder manually`;
1180
+ content += `</p>`;
1181
+
1182
+ document.getElementById('operationContent').innerHTML = content;
1183
+ this.operationsPanel.style.display = 'block';
1184
+ this.contextMenu.style.display = 'none';
1185
+ }
1186
+
1187
+ selectSimilarFiles() {
1188
+ if (!this.selectedFileData || this.selectedFileData.type !== 'file') return;
1189
+
1190
+ const extension = this.selectedFileData.name.toLowerCase().split('.').pop();
1191
+ const similarFiles = this.findFilesByExtension(this.fileData, extension);
1192
+
1193
+ let content = `<div class="operations-header">πŸ” Similar Files (.${extension})</div>`;
1194
+ content += `<p>Found ${similarFiles.length} files with .${extension} extension:</p>`;
1195
+ content += `<div style="max-height: 200px; overflow-y: auto; margin: 10px 0;">`;
1196
+
1197
+ let totalSize = 0;
1198
+ similarFiles.forEach(file => {
1199
+ totalSize += file.size;
1200
+ content += `<div style="padding: 5px; border-bottom: 1px solid #eee; font-size: 12px;">`;
1201
+ content += `<strong>${file.name}</strong><br>`;
1202
+ content += `<span style="color: #666;">${file.path} - ${this.formatFileSize(file.size)}</span>`;
1203
+ content += `</div>`;
1204
+ });
1205
+
1206
+ content += `</div>`;
1207
+ content += `<div class="space-savings">`;
1208
+ content += `πŸ“Š <strong>Total size:</strong> ${this.formatFileSize(totalSize)}`;
1209
+ content += `</div>`;
1210
+
1211
+ document.getElementById('operationContent').innerHTML = content;
1212
+ this.operationsPanel.style.display = 'block';
1213
+ this.contextMenu.style.display = 'none';
1214
+ }
1215
+
1216
+ findFilesByExtension(node, extension) {
1217
+ let files = [];
1218
+
1219
+ if (node.type === 'file' && node.name.toLowerCase().endsWith(`.${extension}`)) {
1220
+ files.push(node);
1221
+ }
1222
+
1223
+ if (node.children) {
1224
+ for (const child of node.children) {
1225
+ files = files.concat(this.findFilesByExtension(child, extension));
1226
+ }
1227
+ }
1228
+
1229
+ return files;
1230
+ }
1231
+
1232
+ countFiles(node) {
1233
+ let count = { files: 0, folders: 0 };
1234
+
1235
+ if (node.type === 'file') {
1236
+ count.files = 1;
1237
+ } else if (node.type === 'directory') {
1238
+ count.folders = 1;
1239
+ if (node.children) {
1240
+ for (const child of node.children) {
1241
+ const childCount = this.countFiles(child);
1242
+ count.files += childCount.files;
1243
+ count.folders += childCount.folders;
1244
+ }
1245
+ }
1246
+ }
1247
+
1248
+ return count;
1249
+ }
1250
+
1251
+ closeOperationsPanel() {
1252
+ this.operationsPanel.style.display = 'none';
1253
+ }
1254
+
1255
+ showNotification(message) {
1256
+ // Create temporary notification
1257
+ const notification = document.createElement('div');
1258
+ notification.style.cssText = `
1259
+ position: fixed;
1260
+ top: 20px;
1261
+ left: 50%;
1262
+ transform: translateX(-50%);
1263
+ background: #28a745;
1264
+ color: white;
1265
+ padding: 10px 20px;
1266
+ border-radius: 5px;
1267
+ z-index: 10001;
1268
+ font-size: 14px;
1269
+ `;
1270
+ notification.textContent = message;
1271
+ document.body.appendChild(notification);
1272
+
1273
+ setTimeout(() => {
1274
+ document.body.removeChild(notification);
1275
+ }, 3000);
1276
  }
1277
 
1278
  createTooltipContent(data) {