Skip to content

Commit 2f3c05d

Browse files
Update inspector.js
1 parent 3fe981d commit 2f3c05d

1 file changed

Lines changed: 24 additions & 34 deletions

File tree

js/inspector.js

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,29 @@ const Inspector = (() => {
1616
function _getSingleRoot(zipFiles) {
1717
const topLevelEntries = new Set();
1818
Object.keys(zipFiles).forEach(path => {
19-
const topLevel = path.split('/')[0];
20-
if (topLevel) topLevelEntries.add(topLevel);
19+
const parts = path.split('/');
20+
// If it has a slash, it's in a folder. If no slash, check if it's a file.
21+
if (parts.length > 1) {
22+
topLevelEntries.add(parts[0]);
23+
} else if (!zipFiles[path].dir) {
24+
topLevelEntries.add('__ROOT_FILE__');
25+
}
2126
});
2227

23-
if (topLevelEntries.size === 1) {
28+
if (topLevelEntries.size === 1 && !topLevelEntries.has('__ROOT_FILE__')) {
2429
const root = Array.from(topLevelEntries)[0];
25-
// Ensure it's actually a directory
26-
if (zipFiles[root + '/']) return root + '/';
30+
// CRITICAL FIX: Never flatten the .github directory!
31+
if (root.toLowerCase() === '.github') return null;
32+
return root + '/';
2733
}
2834
return null;
2935
}
3036

3137
/**
3238
* Inspect a ZIP file and return its contents analysis.
33-
* @param {File|Blob} file
34-
* @returns {Promise<InspectResult>}
3539
*/
3640
async function inspect(file) {
37-
if (typeof JSZip === 'undefined') {
38-
throw new Error('JSZip library not loaded. Check your CDN link.');
39-
}
41+
if (typeof JSZip === 'undefined') throw new Error('JSZip library not loaded.');
4042

4143
const zip = await JSZip.loadAsync(file).catch(err => {
4244
throw new Error(`Failed to read ZIP: ${err.message}`);
@@ -54,7 +56,6 @@ const Inspector = (() => {
5456
const size = entry._data?.uncompressedSize || 0;
5557
totalSize += size;
5658

57-
// Adjust path if the backend will flatten it
5859
let actualPath = relativePath;
5960
if (rootToFlatten && actualPath.startsWith(rootToFlatten)) {
6061
actualPath = actualPath.substring(rootToFlatten.length);
@@ -64,14 +65,13 @@ const Inspector = (() => {
6465
path: actualPath,
6566
name: actualPath.split('/').pop(),
6667
size,
67-
isWorkflow: /^\.github\/workflows\/.*\.ya?ml$/i.test(actualPath),
68+
isWorkflow: /(?:^|\/)\.github\/workflows\/.*\.ya?ml$/i.test(actualPath),
6869
};
6970

7071
files.push(info);
7172
if (info.isWorkflow) workflowFiles.push(info);
7273
});
7374

74-
// Sort by path
7575
files.sort((a, b) => a.path.localeCompare(b.path));
7676

7777
return {
@@ -87,9 +87,7 @@ const Inspector = (() => {
8787
* Create a ZIP blob from a FileList (single or multiple files).
8888
*/
8989
async function createZipFromFiles(fileList) {
90-
if (typeof JSZip === 'undefined') {
91-
throw new Error('JSZip library not loaded.');
92-
}
90+
if (typeof JSZip === 'undefined') throw new Error('JSZip library not loaded.');
9391

9492
const zip = new JSZip();
9593
for (const file of fileList) {
@@ -103,16 +101,19 @@ const Inspector = (() => {
103101
* Create a ZIP blob from folder entries (from webkitdirectory input).
104102
*/
105103
async function createZipFromFolder(fileList) {
106-
if (typeof JSZip === 'undefined') {
107-
throw new Error('JSZip library not loaded.');
108-
}
104+
if (typeof JSZip === 'undefined') throw new Error('JSZip library not loaded.');
109105

110106
const zip = new JSZip();
111107
for (const file of fileList) {
112108
const path = file.webkitRelativePath || file.name;
113-
// Remove the root folder name to flatten
114109
const parts = path.split('/');
115-
const relativePath = parts.length > 1 ? parts.slice(1).join('/') : path;
110+
let relativePath = path;
111+
112+
// CRITICAL FIX: Only flatten if the root folder is NOT .github
113+
if (parts.length > 1 && parts[0].toLowerCase() !== '.github') {
114+
relativePath = parts.slice(1).join('/');
115+
}
116+
116117
const data = await file.arrayBuffer();
117118
zip.file(relativePath, data);
118119
}
@@ -121,12 +122,9 @@ const Inspector = (() => {
121122

122123
/**
123124
* Extract workflow file contents from a ZIP for direct API push.
124-
* Returns an array of { path, contentBase64 } objects.
125125
*/
126126
async function extractWorkflowFiles(file) {
127-
if (typeof JSZip === 'undefined') {
128-
throw new Error('JSZip library not loaded.');
129-
}
127+
if (typeof JSZip === 'undefined') throw new Error('JSZip library not loaded.');
130128

131129
const zip = await JSZip.loadAsync(file);
132130
const results = [];
@@ -135,13 +133,12 @@ const Inspector = (() => {
135133
for (const [path, entry] of Object.entries(zip.files)) {
136134
if (entry.dir) continue;
137135

138-
// Adjust path if the backend will flatten it
139136
let actualPath = path;
140137
if (rootToFlatten && actualPath.startsWith(rootToFlatten)) {
141138
actualPath = actualPath.substring(rootToFlatten.length);
142139
}
143140

144-
if (/^\.github\/workflows\/.*\.ya?ml$/i.test(actualPath)) {
141+
if (/(?:^|\/)\.github\/workflows\/.*\.ya?ml$/i.test(actualPath)) {
145142
const content = await entry.async('base64');
146143
results.push({ path: actualPath, contentBase64: content });
147144
}
@@ -161,14 +158,12 @@ const Inspector = (() => {
161158
return;
162159
}
163160

164-
// Build a tree structure
165161
const tree = {};
166162
result.files.forEach(f => {
167163
const parts = f.path.split('/');
168164
let node = tree;
169165
parts.forEach((part, i) => {
170166
if (i === parts.length - 1) {
171-
// File leaf
172167
if (!node.__files) node.__files = [];
173168
node.__files.push(f);
174169
} else {
@@ -178,7 +173,6 @@ const Inspector = (() => {
178173
});
179174
});
180175

181-
// Stats bar
182176
const statsHtml = `
183177
<div class="inspector-stats">
184178
<span class="inspector-stat"><i data-lucide="file" class="inspector-stat-icon"></i>${result.fileCount} files</span>
@@ -189,14 +183,12 @@ const Inspector = (() => {
189183
</div>
190184
`;
191185

192-
// Render the file tree
193186
const treeHtml = _renderNode(tree, '');
194187

195188
container.innerHTML = statsHtml + `<div class="inspector-tree">${treeHtml}</div>`;
196189

197190
if (typeof lucide !== 'undefined') lucide.createIcons({ nodes: [container] });
198191

199-
// Bind collapsible folders
200192
container.querySelectorAll('.inspector-folder-toggle').forEach(btn => {
201193
btn.addEventListener('click', () => {
202194
const parent = btn.closest('.inspector-folder');
@@ -208,7 +200,6 @@ const Inspector = (() => {
208200
function _renderNode(node, prefix) {
209201
let html = '';
210202

211-
// Render directories first
212203
const dirs = Object.keys(node).filter(k => k !== '__files').sort();
213204
dirs.forEach(dir => {
214205
const childCount = _countFiles(node[dir]);
@@ -227,7 +218,6 @@ const Inspector = (() => {
227218
`;
228219
});
229220

230-
// Render files
231221
const files = node.__files || [];
232222
files.forEach(f => {
233223
const sizeClass = f.size > 10_000_000 ? 'size-danger' :

0 commit comments

Comments
 (0)