snapexcel 1.1.0 → 1.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snapexcel",
3
- "version": "1.1.0",
3
+ "version": "1.2.2",
4
4
  "description": "Desktop Screenshot Logger for UI Testing - Capture any window including Android emulators",
5
5
  "main": "src/main.js",
6
6
  "bin": {
package/src/index.html CHANGED
@@ -11,13 +11,19 @@
11
11
  <!-- Header -->
12
12
  <header class="header">
13
13
  <div class="logo">
14
- <span class="logo-icon">📸</span>
14
+ <div class="logo-icon-wrapper">
15
+ <svg class="logo-icon-svg" width="28" height="28" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
16
+ <rect x="2" y="5" width="20" height="14" rx="2" stroke="white" stroke-width="1.5"/>
17
+ <circle cx="12" cy="12" r="3.5" stroke="white" stroke-width="1.5"/>
18
+ <circle cx="12" cy="12" r="1.5" fill="white"/>
19
+ <path d="M7 5V4C7 3.44772 7.44772 3 8 3H10" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
20
+ <circle cx="17" cy="8" r="1" fill="#FFC107"/>
21
+ <path d="M2 8H4" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
22
+ </svg>
23
+ </div>
15
24
  <h1>Screenshot Logger</h1>
16
25
  </div>
17
- <div class="header-actions">
18
- <button id="btn-minimize-header" class="btn-icon btn-minimize-header hidden" title="Minimize">➖</button>
19
- </div>
20
- </header>
26
+ </header>
21
27
 
22
28
  <!-- Setup View -->
23
29
  <div id="setup-view" class="view">
@@ -39,8 +45,8 @@
39
45
  <path d="M13 15.5H16V18H13" stroke="#217346" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
40
46
  </svg>
41
47
  </div>
42
- <h2>Start New Session</h2>
43
- <p>Capture screenshots and export to Excel</p>
48
+ <h2>New Capture Session</h2>
49
+ <p>Document your workflow with screenshots</p>
44
50
 
45
51
  <div class="form-group">
46
52
  <label for="file-name">Excel File Name</label>
@@ -53,15 +59,28 @@
53
59
  </div>
54
60
 
55
61
  <button id="btn-start-session" class="btn btn-primary btn-large">
56
- Start Session
62
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-right: 6px;">
63
+ <circle cx="12" cy="12" r="9" stroke="white" stroke-width="2"/>
64
+ <circle cx="12" cy="12" r="3" fill="white"/>
65
+ <path d="M12 5V3" stroke="white" stroke-width="2" stroke-linecap="round"/>
66
+ <path d="M12 21V19" stroke="white" stroke-width="2" stroke-linecap="round"/>
67
+ <path d="M5 12H3" stroke="white" stroke-width="2" stroke-linecap="round"/>
68
+ <path d="M21 12H19" stroke="white" stroke-width="2" stroke-linecap="round"/>
69
+ </svg>
70
+ Start Capturing
57
71
  </button>
58
72
 
59
73
  <div class="divider-text">
60
74
  <span>or</span>
61
75
  </div>
62
76
 
63
- <button id="btn-import-excel" class="btn btn-secondary btn-large">
64
- 📂 Import Existing Excel
77
+ <button id="btn-import-excel" class="btn-import-excel">
78
+ <svg class="btn-import-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
79
+ <path d="M14 2H6C4.89543 2 4 2.89543 4 4V20C4 21.1046 4.89543 22 6 22H18C19.1046 22 20 21.1046 20 20V8L14 2Z" fill="white" stroke="white" stroke-width="0.5"/>
80
+ <path d="M14 2V8H20" fill="rgba(255,255,255,0.8)" stroke="white" stroke-width="0.5"/>
81
+ <text x="12" y="16" text-anchor="middle" fill="#185c37" font-size="7" font-weight="bold" font-family="Arial">XLS</text>
82
+ </svg>
83
+ Open Excel
65
84
  </button>
66
85
  </div>
67
86
  </div>
@@ -93,13 +112,6 @@
93
112
  </div>
94
113
  </div>
95
114
 
96
- <!-- Secondary Actions -->
97
- <div class="secondary-actions">
98
- <button id="btn-note" class="btn btn-secondary btn-full" title="Add Note">
99
- 📝 Add Note
100
- </button>
101
- </div>
102
-
103
115
  <!-- Screenshots Grid -->
104
116
  <div class="screenshots-section">
105
117
  <div class="section-header">
package/src/renderer.js CHANGED
@@ -33,17 +33,11 @@ const elements = {
33
33
  btnStartSession: document.getElementById('btn-start-session'),
34
34
  btnImportExcel: document.getElementById('btn-import-excel'),
35
35
 
36
- // Header
37
- btnMinimizeHeader: document.getElementById('btn-minimize-header'),
38
-
39
36
  // Session Info
40
37
  currentFileName: document.getElementById('current-file-name'),
41
38
  sheetSelect: document.getElementById('sheet-select'),
42
39
  btnAddSheet: document.getElementById('btn-add-sheet'),
43
40
 
44
- // Actions
45
- btnNote: document.getElementById('btn-note'),
46
-
47
41
  // Screenshots
48
42
  screenshotsGrid: document.getElementById('screenshots-grid'),
49
43
  screenshotCount: document.getElementById('screenshot-count'),
@@ -276,15 +270,6 @@ function setupEventListeners() {
276
270
  if (e.key === 'Enter') startSession();
277
271
  });
278
272
 
279
- // Header
280
- elements.btnMinimizeHeader.addEventListener('click', showMinimalView);
281
-
282
- // Minimal view capture
283
- elements.btnCaptureMinimal.addEventListener('click', captureScreenshot);
284
-
285
- // Secondary Actions
286
- elements.btnNote.addEventListener('click', openNoteModal);
287
-
288
273
  // Sheet Management
289
274
  elements.sheetSelect.addEventListener('change', switchSheet);
290
275
  elements.btnAddSheet.addEventListener('click', openSheetModal);
@@ -299,6 +284,14 @@ function setupEventListeners() {
299
284
  elements.btnCancelNote.addEventListener('click', closeNoteModal);
300
285
  elements.btnSaveNote.addEventListener('click', saveNote);
301
286
 
287
+ // Save note on Ctrl+Enter or Cmd+Enter
288
+ elements.noteText.addEventListener('keydown', (e) => {
289
+ if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
290
+ e.preventDefault();
291
+ saveNote();
292
+ }
293
+ });
294
+
302
295
  // Preview Modal
303
296
  elements.btnPreviewClose.addEventListener('click', closePreview);
304
297
  elements.btnPreviewPrev.addEventListener('click', showPrevPreview);
@@ -333,30 +326,15 @@ function setupEventListeners() {
333
326
  function showSetupView() {
334
327
  elements.setupView.classList.remove('hidden');
335
328
  elements.captureView.classList.add('hidden');
336
- elements.minimalView.classList.add('hidden');
337
- elements.btnMinimizeHeader.classList.add('hidden');
338
329
  elements.fileNameInput.focus();
339
330
  }
340
331
 
341
332
  function showCaptureView() {
342
333
  elements.setupView.classList.add('hidden');
343
334
  elements.captureView.classList.remove('hidden');
344
- elements.minimalView.classList.add('hidden');
345
- elements.btnMinimizeHeader.classList.remove('hidden');
346
- state.isMinimalView = false;
347
335
  updateUI();
348
336
  }
349
337
 
350
- function showMinimalView() {
351
- elements.setupView.classList.add('hidden');
352
- elements.captureView.classList.add('hidden');
353
- elements.minimalView.classList.remove('hidden');
354
- elements.btnMinimizeHeader.classList.add('hidden');
355
- state.isMinimalView = true;
356
- ipcRenderer.invoke('set-window-size', { width: 300, height: 180 });
357
- updateMinimalUI();
358
- }
359
-
360
338
  // ==================== Session Management ====================
361
339
  function startSession() {
362
340
  const fileName = elements.fileNameInput.value.trim();
@@ -1045,6 +1023,27 @@ function handlePreviewKeyboard(e) {
1045
1023
  // Only handle if preview is open
1046
1024
  if (state.previewIndex < 0) return;
1047
1025
 
1026
+ // Don't handle keyboard shortcuts if user is typing in an input or textarea
1027
+ const activeElement = document.activeElement;
1028
+ const isTyping = activeElement && (
1029
+ activeElement.tagName === 'INPUT' ||
1030
+ activeElement.tagName === 'TEXTAREA' ||
1031
+ activeElement.isContentEditable
1032
+ );
1033
+
1034
+ // If note modal is open, only handle Escape to close it
1035
+ if (!elements.noteModal.classList.contains('hidden')) {
1036
+ if (e.key === 'Escape') {
1037
+ closeNoteModal();
1038
+ e.preventDefault();
1039
+ }
1040
+ // Don't process other keys when note modal is open
1041
+ return;
1042
+ }
1043
+
1044
+ // If user is typing somewhere, don't intercept
1045
+ if (isTyping) return;
1046
+
1048
1047
  switch (e.key) {
1049
1048
  case 'Escape':
1050
1049
  closePreview();
@@ -1060,9 +1059,9 @@ function handlePreviewKeyboard(e) {
1060
1059
  openNoteFromPreview();
1061
1060
  break;
1062
1061
  case 'Delete':
1063
- case 'Backspace':
1064
1062
  deleteFromPreview();
1065
1063
  break;
1064
+ // Don't capture Backspace - it's used for typing
1066
1065
  }
1067
1066
  }
1068
1067
 
@@ -1282,7 +1281,7 @@ function generatePDFHTML() {
1282
1281
  .screenshot-row:hover { background: #E6F2FC; }
1283
1282
  .col-num { width: 50px; text-align: center; display: flex; align-items: center; justify-content: center; }
1284
1283
  .col-screenshot { width: 420px; }
1285
- .col-notes { flex: 1; font-size: 12px; padding-left: 15px; }
1284
+ .col-notes { flex: 1; font-size: 12px; padding-left: 15px; word-wrap: break-word; overflow-wrap: break-word; word-break: break-word; max-width: 400px; }
1286
1285
  .timestamp {
1287
1286
  font-size: 11px;
1288
1287
  color: #666;
@@ -1438,11 +1437,12 @@ function generateHTML() {
1438
1437
  .content { padding: 30px 40px; }
1439
1438
  .sheet { margin-bottom: 40px; }
1440
1439
  .sheet h2 { color: #041E42; font-size: 20px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 3px solid #0071DC; }
1441
- table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
1440
+ table { width: 100%; border-collapse: collapse; margin-bottom: 20px; table-layout: fixed; }
1442
1441
  th { background: #041E42; color: white; padding: 12px 16px; text-align: left; font-weight: 600; }
1443
- td { padding: 12px 16px; border-bottom: 1px solid #e0e0e0; vertical-align: top; }
1442
+ td { padding: 12px 16px; border-bottom: 1px solid #e0e0e0; vertical-align: top; word-wrap: break-word; overflow-wrap: break-word; }
1444
1443
  tr:hover { background: #E6F2FC; }
1445
1444
  .timestamp { font-size: 12px; color: #666; margin-bottom: 8px; }
1445
+ td:nth-child(3) { max-width: 400px; word-break: break-word; }
1446
1446
  .screenshot-img:hover { transform: scale(1.02); box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
1447
1447
  .footer { background: #041E42; color: white; padding: 20px 40px; text-align: center; font-size: 13px; }
1448
1448
 
@@ -1617,10 +1617,11 @@ async function generateXLSX() {
1617
1617
  // xl/styles.xml - Dark Blue Corporate Theme with timestamp style
1618
1618
  zip.file('xl/styles.xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1619
1619
  <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
1620
- <fonts count="3">
1620
+ <fonts count="4">
1621
1621
  <font><sz val="11"/><name val="Calibri"/></font>
1622
1622
  <font><b/><sz val="11"/><color rgb="FFFFFFFF"/><name val="Calibri"/></font>
1623
1623
  <font><sz val="10"/><color rgb="FF666666"/><name val="Calibri"/></font>
1624
+ <font><sz val="16"/><name val="Calibri"/></font>
1624
1625
  </fonts>
1625
1626
  <fills count="3">
1626
1627
  <fill><patternFill patternType="none"/></fill>
@@ -1632,11 +1633,12 @@ async function generateXLSX() {
1632
1633
  <border><left style="thin"><color rgb="FF041E42"/></left><right style="thin"><color rgb="FF041E42"/></right><top style="thin"><color rgb="FF041E42"/></top><bottom style="thin"><color rgb="FF041E42"/></bottom></border>
1633
1634
  </borders>
1634
1635
  <cellStyleXfs count="1"><xf/></cellStyleXfs>
1635
- <cellXfs count="4">
1636
+ <cellXfs count="5">
1636
1637
  <xf/>
1637
1638
  <xf fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1"><alignment horizontal="center" vertical="center"/></xf>
1638
1639
  <xf borderId="1" applyBorder="1" applyAlignment="1"><alignment vertical="top" wrapText="1"/></xf>
1639
1640
  <xf fontId="2" borderId="1" applyFont="1" applyBorder="1" applyAlignment="1"><alignment vertical="top" wrapText="1"/></xf>
1641
+ <xf fontId="3" borderId="1" applyFont="1" applyBorder="1" applyAlignment="1"><alignment vertical="top" wrapText="1"/></xf>
1640
1642
  </cellXfs>
1641
1643
  </styleSheet>`);
1642
1644
 
@@ -1707,12 +1709,12 @@ async function generateXLSX() {
1707
1709
  // Row height: 540 for landscape (~7.5 inches), 720 for portrait (~10 inches)
1708
1710
  const rowHeight = isPortrait ? 720 : 540;
1709
1711
 
1710
- // Timestamp shown at top of cell B (above image), Notes in column C
1712
+ // Timestamp shown at top of cell B (above image), Notes in column C with 16pt font
1711
1713
  sheetXml += `
1712
1714
  <row r="${rowNum}" ht="${rowHeight}" customHeight="1">
1713
1715
  <c r="A${rowNum}" s="2"><v>${i + 1}</v></c>
1714
1716
  <c r="B${rowNum}" s="3" t="s"><v>${stringIndex[timestamp]}</v></c>
1715
- <c r="C${rowNum}" s="2" t="s"><v>${stringIndex[notes]}</v></c>
1717
+ <c r="C${rowNum}" s="4" t="s"><v>${stringIndex[notes]}</v></c>
1716
1718
  </row>`;
1717
1719
  }
1718
1720
 
package/src/styles.css CHANGED
@@ -66,17 +66,36 @@ body {
66
66
  .logo {
67
67
  display: flex;
68
68
  align-items: center;
69
- gap: 8px;
69
+ gap: 10px;
70
70
  }
71
71
 
72
- .logo-icon {
73
- font-size: 20px;
72
+ .logo-icon-wrapper {
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ width: 38px;
77
+ height: 38px;
78
+ background: rgba(255, 255, 255, 0.15);
79
+ border-radius: 10px;
80
+ backdrop-filter: blur(4px);
81
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
82
+ transition: all 0.3s ease;
83
+ }
84
+
85
+ .logo-icon-wrapper:hover {
86
+ background: rgba(255, 255, 255, 0.25);
87
+ transform: scale(1.05);
88
+ }
89
+
90
+ .logo-icon-svg {
91
+ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
74
92
  }
75
93
 
76
94
  .logo h1 {
77
- font-size: 15px;
95
+ font-size: 16px;
78
96
  font-weight: 600;
79
97
  letter-spacing: -0.3px;
98
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
80
99
  }
81
100
 
82
101
  .header-actions {
@@ -124,6 +143,17 @@ body {
124
143
  color: white;
125
144
  }
126
145
 
146
+ .btn-floating-mode {
147
+ background: rgba(255, 255, 255, 0.2);
148
+ border: none;
149
+ color: white;
150
+ }
151
+
152
+ .btn-floating-mode:hover {
153
+ background: rgba(255, 255, 255, 0.35);
154
+ transform: scale(1.05);
155
+ }
156
+
127
157
  /* Views */
128
158
  .view {
129
159
  display: flex;
@@ -500,39 +530,68 @@ body {
500
530
  display: flex;
501
531
  flex-direction: column;
502
532
  margin-bottom: 12px;
533
+ background: linear-gradient(145deg, #f4f6f9 0%, #eaeff4 100%);
534
+ border-radius: 5px;
535
+ border: 0.5px solid rgba(4, 30, 66, 0.04);
536
+ padding: 5px;
503
537
  }
504
538
 
505
539
  .section-header {
506
540
  display: flex;
507
541
  align-items: center;
508
542
  justify-content: space-between;
509
- font-size: 12px;
543
+ font-size: 11px;
510
544
  font-weight: 600;
511
- color: var(--text-secondary);
512
- margin-bottom: 10px;
545
+ color: var(--primary-color);
546
+ margin-bottom: 5px;
547
+ padding: 0 3px 3px 3px;
548
+ border-bottom: 0.5px solid rgba(4, 30, 66, 0.04);
513
549
  }
514
550
 
515
551
  .badge {
516
552
  background: var(--primary-color);
517
553
  color: white;
518
- padding: 2px 8px;
519
- border-radius: 10px;
520
- font-size: 11px;
554
+ padding: 2px 7px;
555
+ border-radius: 8px;
556
+ font-size: 10px;
521
557
  font-weight: 600;
522
558
  }
523
559
 
524
560
  .screenshots-grid {
525
561
  display: grid;
526
562
  grid-template-columns: repeat(4, 1fr);
527
- gap: 10px;
563
+ gap: 8px;
528
564
  overflow-y: auto;
529
- padding: 10px;
530
- background: var(--background-secondary);
531
- border-radius: var(--border-radius-sm);
532
- min-height: 80px;
565
+ padding: 5px;
566
+ background: rgba(255, 255, 255, 0.5);
567
+ border-radius: 3px;
568
+ min-height: 70px;
533
569
  max-height: 200px;
534
570
  }
535
571
 
572
+ /* Ultra thin scrollbar - both vertical and horizontal */
573
+ .screenshots-grid::-webkit-scrollbar {
574
+ width: 3px;
575
+ height: 3px;
576
+ }
577
+
578
+ .screenshots-grid::-webkit-scrollbar-track {
579
+ background: transparent;
580
+ }
581
+
582
+ .screenshots-grid::-webkit-scrollbar-thumb {
583
+ background: rgba(4, 30, 66, 0.15);
584
+ border-radius: 2px;
585
+ }
586
+
587
+ .screenshots-grid::-webkit-scrollbar-thumb:hover {
588
+ background: rgba(4, 30, 66, 0.3);
589
+ }
590
+
591
+ .screenshots-grid::-webkit-scrollbar-corner {
592
+ background: transparent;
593
+ }
594
+
536
595
  .empty-state {
537
596
  grid-column: 1 / -1;
538
597
  display: flex;
@@ -862,7 +921,7 @@ body {
862
921
  align-items: center;
863
922
  width: 100%;
864
923
  max-width: 280px;
865
- margin: 16px 0;
924
+ margin: 20px 0 16px 0;
866
925
  }
867
926
 
868
927
  .divider-text::before,
@@ -879,6 +938,41 @@ body {
879
938
  font-size: 12px;
880
939
  }
881
940
 
941
+ /* Import Excel Button - Rich Excel Green Theme */
942
+ .btn-import-excel {
943
+ display: inline-flex;
944
+ align-items: center;
945
+ justify-content: center;
946
+ gap: 10px;
947
+ width: 100%;
948
+ max-width: 280px;
949
+ padding: 14px 28px;
950
+ background: linear-gradient(135deg, #217346 0%, #185c37 100%);
951
+ border: none;
952
+ border-radius: 10px;
953
+ color: white;
954
+ font-size: 15px;
955
+ font-weight: 600;
956
+ cursor: pointer;
957
+ transition: all 0.25s ease;
958
+ box-shadow: 0 3px 10px rgba(33, 115, 70, 0.35);
959
+ }
960
+
961
+ .btn-import-excel:hover {
962
+ background: linear-gradient(135deg, #28a745 0%, #217346 100%);
963
+ transform: translateY(-2px);
964
+ box-shadow: 0 6px 16px rgba(33, 115, 70, 0.45);
965
+ }
966
+
967
+ .btn-import-excel:active {
968
+ transform: translateY(0);
969
+ box-shadow: 0 3px 10px rgba(33, 115, 70, 0.35);
970
+ }
971
+
972
+ .btn-import-icon {
973
+ flex-shrink: 0;
974
+ }
975
+
882
976
  /* Fullscreen Preview Modal */
883
977
  .preview-modal {
884
978
  position: fixed;