Skip to content

Commit ab21c9e

Browse files
authored
Polish the demo MSI viewer styling (nightlark#60)
* Polish the demo MSI viewer styling * Show currently loaded file name * Use the loaded msi file name to give a better name to the extracted files zip
1 parent 09e7f58 commit ab21c9e

File tree

2 files changed

+113
-11
lines changed

2 files changed

+113
-11
lines changed

docs/_static/msi_viewer.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class MSIViewer {
77
this.pymsi = null;
88
this.currentPackage = null;
99
this.currentMsi = null;
10+
this.currentFileName = null;
1011
this.initElements();
1112
this.initEventListeners();
1213
this.loadPyodide();
@@ -17,6 +18,7 @@ class MSIViewer {
1718
this.fileInput = document.getElementById('msi-file-input');
1819
this.loadingIndicator = document.getElementById('loading-indicator');
1920
this.msiContent = document.getElementById('msi-content');
21+
this.currentFileDisplay = document.getElementById('current-file-display');
2022
this.extractButton = document.getElementById('extract-button');
2123
this.filesList = document.getElementById('files-list');
2224
this.tableSelector = document.getElementById('table-selector');
@@ -103,6 +105,7 @@ class MSIViewer {
103105
if (!this.fileInput.files || this.fileInput.files.length === 0) return;
104106

105107
const file = this.fileInput.files[0];
108+
this.currentFileName = file.name;
106109
this.loadingIndicator.style.display = 'block';
107110
this.loadingIndicator.textContent = 'Reading MSI file...';
108111

@@ -136,7 +139,11 @@ class MSIViewer {
136139
await this.loadStreams();
137140
console.log('Streams loaded successfully');
138141

139-
this.msiContent.style.display = 'block';
142+
// Enable the extract button and show current file
143+
this.extractButton.disabled = false;
144+
this.currentFileDisplay.textContent = `Currently loaded: ${this.currentFileName}`;
145+
this.currentFileDisplay.style.display = 'block';
146+
140147
this.loadingIndicator.style.display = 'none';
141148
} catch (error) {
142149
this.loadingIndicator.textContent = `Error processing MSI file: ${error.message}`;
@@ -418,11 +425,15 @@ class MSIViewer {
418425
// Generate ZIP blob
419426
const zipBlob = await zip.generateAsync({ type: 'blob' });
420427

428+
// Create filename based on MSI name
429+
const baseFileName = this.currentFileName.replace(/\.msi$/i, '');
430+
const zipFileName = `${baseFileName}_extracted.zip`;
431+
421432
// Trigger download
422433
const url = URL.createObjectURL(zipBlob);
423434
const a = document.createElement('a');
424435
a.href = url;
425-
a.download = 'extracted_files.zip';
436+
a.download = zipFileName;
426437
document.body.appendChild(a);
427438
a.click();
428439

docs/msi_viewer.md

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@ Behind the scenes, it is running [pymsi](https://github.com/nightlark/pymsi/) us
66

77
<div id="msi-viewer-app">
88
<div class="file-selector">
9-
<h2>Select an MSI File</h2>
10-
<input type="file" id="msi-file-input" accept=".msi" />
9+
<div class="file-input-container">
10+
<input type="file" id="msi-file-input" accept=".msi" />
11+
<label for="msi-file-input" class="file-input-label">
12+
<span class="file-input-text">Choose MSI File</span>
13+
<span class="file-input-icon">📁</span>
14+
</label>
15+
</div>
1116
<div id="loading-indicator" style="display: none;">Loading...</div>
1217
</div>
1318

14-
<div id="msi-content" style="display: none;">
19+
<div id="msi-content">
20+
<div id="current-file-display" style="display: none;"></div>
1521
<div class="tabs">
1622
<button class="tab-button active" data-tab="files">Files</button>
1723
<button class="tab-button" data-tab="tables">Tables</button>
@@ -21,7 +27,7 @@ Behind the scenes, it is running [pymsi](https://github.com/nightlark/pymsi/) us
2127
<div class="tab-content">
2228
<div id="files-tab" class="tab-pane active">
2329
<h3>Files</h3>
24-
<button id="extract-button">Extract All Files (ZIP)</button>
30+
<button id="extract-button" disabled>Extract All Files (ZIP)</button>
2531
<div id="files-list-container">
2632
<table id="files-table">
2733
<thead>
@@ -33,27 +39,35 @@ Behind the scenes, it is running [pymsi](https://github.com/nightlark/pymsi/) us
3339
<th>Version</th>
3440
</tr>
3541
</thead>
36-
<tbody id="files-list"></tbody>
42+
<tbody id="files-list">
43+
<tr><td colspan="5" class="empty-message">Select an MSI file to view its contents</td></tr>
44+
</tbody>
3745
</table>
3846
</div>
3947
</div>
4048
<div id="tables-tab" class="tab-pane">
4149
<h3>Tables</h3>
42-
<select id="table-selector"></select>
50+
<select id="table-selector"><option>Select an MSI file first</option></select>
4351
<div id="table-viewer-container">
4452
<table id="table-viewer">
4553
<thead id="table-header"></thead>
46-
<tbody id="table-content"></tbody>
54+
<tbody id="table-content">
55+
<tr><td class="empty-message">Select an MSI file to view table data</td></tr>
56+
</tbody>
4757
</table>
4858
</div>
4959
</div>
5060
<div id="summary-tab" class="tab-pane">
5161
<h3>Summary Information</h3>
52-
<div id="summary-content"></div>
62+
<div id="summary-content">
63+
<p class="empty-message">Select an MSI file to view summary information</p>
64+
</div>
5365
</div>
5466
<div id="streams-tab" class="tab-pane">
5567
<h3>Streams</h3>
56-
<div id="streams-content"></div>
68+
<div id="streams-content">
69+
<p class="empty-message">Select an MSI file to view streams</p>
70+
</div>
5771
</div>
5872
</div>
5973
</div>
@@ -66,6 +80,71 @@ Behind the scenes, it is running [pymsi](https://github.com/nightlark/pymsi/) us
6680
margin: 0 auto;
6781
}
6882

83+
.file-selector {
84+
text-align: center;
85+
padding: 2rem;
86+
background: #f9f9f9;
87+
border-radius: 8px;
88+
margin-bottom: 2rem;
89+
}
90+
91+
.file-input-container {
92+
position: relative;
93+
display: inline-block;
94+
}
95+
96+
#msi-file-input {
97+
position: absolute;
98+
opacity: 0;
99+
width: 100%;
100+
height: 100%;
101+
cursor: pointer;
102+
}
103+
104+
.file-input-label {
105+
display: inline-flex;
106+
align-items: center;
107+
gap: 0.5rem;
108+
padding: 0.75rem 1.5rem;
109+
background: #007acc;
110+
color: white;
111+
border-radius: 6px;
112+
cursor: pointer;
113+
font-weight: 500;
114+
transition: background-color 0.2s ease;
115+
border: 2px solid transparent;
116+
}
117+
118+
.file-input-label:hover {
119+
background: #005a9e;
120+
}
121+
122+
.file-input-label:focus-within {
123+
outline: 2px solid #007acc;
124+
outline-offset: 2px;
125+
}
126+
127+
#loading-indicator {
128+
margin-top: 1rem;
129+
padding: 0.5rem;
130+
background: #e3f2fd;
131+
border: 1px solid #90caf9;
132+
border-radius: 4px;
133+
color: #1565c0;
134+
font-weight: 500;
135+
}
136+
137+
#current-file-display {
138+
margin-bottom: 1rem;
139+
padding: 0.5rem 1rem;
140+
background: #f0f8ff;
141+
border: 1px solid #b0d4f1;
142+
border-radius: 4px;
143+
color: #2c5282;
144+
font-weight: 500;
145+
text-align: center;
146+
}
147+
69148
.tabs {
70149
display: flex;
71150
margin-bottom: 1rem;
@@ -118,6 +197,18 @@ Behind the scenes, it is running [pymsi](https://github.com/nightlark/pymsi/) us
118197
cursor: pointer;
119198
}
120199

200+
#extract-button:disabled {
201+
background: #cccccc;
202+
cursor: not-allowed;
203+
}
204+
205+
.empty-message {
206+
text-align: center;
207+
color: #666;
208+
font-style: italic;
209+
padding: 2rem;
210+
}
211+
121212
#files-list-container, #table-viewer-container {
122213
max-height: 500px;
123214
overflow-y: auto;

0 commit comments

Comments
 (0)