Skip to content

Commit ec14449

Browse files
Fix fresh-install blank settings and add startup guards
1 parent f051db3 commit ec14449

5 files changed

Lines changed: 69 additions & 8 deletions

File tree

13.9 MB
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
66f91bc29c12a9bf13ce50b4cc549abb11a3d847b3204e95c0f819cdb9055cec folderview.plus-2026.03.08.04.txz

folderview.plus.plg

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,24 @@
66
<!ENTITY launch "Settings/FolderViewPlus">
77
<!ENTITY plugdir "/usr/local/emhttp/plugins/&name;">
88
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/main/folderview.plus.plg">
9-
<!ENTITY version "2026.03.08.02">
10-
<!ENTITY md5 "2b970f8206b92b1608b6e6dfb4cdfb3d">
9+
<!ENTITY version "2026.03.08.04">
10+
<!ENTITY md5 "2e71f08fbeb23ce32587c25abd680b71">
1111
]>
1212

1313
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;" pluginURL="&pluginURL;" icon="folder-icon.png" support="https://github.com/alexphillips-dev/FolderView-Plus/issues" min="7.0.0">
1414
<CHANGES>
1515

16+
###2026.03.08.04
17+
- Bug fix: resolved a first-install regression that could render the Settings page blank.
18+
- Reliability: added startup safeguards so basic Docker/VM sections stay visible even if initial API calls fail.
19+
- Stability: refresh now falls back to empty/default data on first-load fetch hiccups instead of aborting UI rendering.
20+
- Regression guard: added tests to prevent future blank-first-run settings regressions.
21+
22+
23+
###2026.03.08.03
24+
- Maintenance: automated release metadata update.
25+
26+
1627
###2026.03.08.02
1728
- UI/UX fix: advanced side-by-side modules now use adaptive panel sizing instead of forcing full-height blocks.
1829
- Layout fix: advanced module grids now align cards to the top so shorter cards no longer stretch and show unnecessary blank space.

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.js

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,31 @@ const getSectionSearchHaystack = (section) => section.nodes
687687
.join(' ')
688688
.toLowerCase();
689689

690-
const isBasicWorkspaceSection = (section) => BASIC_WORKSPACE_SECTION_KEYS.has(String(section?.key || ''));
690+
const sectionContainsSelector = (section, selector) => {
691+
const nodes = Array.isArray(section?.nodes) ? section.nodes : [];
692+
for (const node of nodes) {
693+
if (!(node instanceof Element)) {
694+
continue;
695+
}
696+
if (typeof node.matches === 'function' && node.matches(selector)) {
697+
return true;
698+
}
699+
if (typeof node.querySelector === 'function' && node.querySelector(selector)) {
700+
return true;
701+
}
702+
}
703+
return false;
704+
};
705+
706+
const isBasicWorkspaceSection = (section) => {
707+
const key = String(section?.key || '').trim().toLowerCase();
708+
if (BASIC_WORKSPACE_SECTION_KEYS.has(key)) {
709+
return true;
710+
}
711+
return sectionContainsSelector(section, 'tbody#docker, tbody#vms');
712+
};
713+
714+
const getBasicWorkspaceSections = () => settingsUiState.sections.filter((section) => isBasicWorkspaceSection(section));
691715

692716
const getVisibleSections = () => settingsUiState.sections.filter((section) => {
693717
const modeVisible = settingsUiState.mode === 'advanced'
@@ -811,6 +835,11 @@ const toggleAdvancedSectionByKey = (sectionKey) => {
811835

812836
const applySettingsSectionVisibility = () => {
813837
const visibleKeys = new Set(getVisibleSections().map((section) => section.key));
838+
if (!visibleKeys.size && settingsUiState.mode === 'basic' && !settingsUiState.query) {
839+
for (const section of getBasicWorkspaceSections()) {
840+
visibleKeys.add(section.key);
841+
}
842+
}
814843
const forceExpandForQuery = Boolean(settingsUiState.query);
815844

816845
for (const section of settingsUiState.sections) {
@@ -6889,17 +6918,17 @@ const renderPerformanceDiagnostics = () => {
68896918
const refreshType = async (type) => {
68906919
const startedAt = perfNowMs();
68916920
const [folders, prefs, info] = await Promise.all([
6892-
fetchFolders(type),
6893-
fetchPrefs(type),
6921+
fetchFolders(type).catch(() => ({})),
6922+
fetchPrefs(type).catch(() => (utils.normalizePrefs({}))),
68946923
fetchTypeInfo(type).catch(() => ({}))
68956924
]);
68966925

68976926
prefsByType[type] = utils.normalizePrefs(prefs || {});
68986927
infoByType[type] = info && typeof info === 'object' ? info : {};
6899-
setTypeFolders(type, folders);
6928+
setTypeFolders(type, utils.normalizeFolderMap(folders || {}));
69006929
renderTable(type);
69016930
recordPerformanceDiagnosticsSample('refresh', type, perfNowMs() - startedAt, {
6902-
folderCount: Object.keys(folders || {}).length,
6931+
folderCount: Object.keys(utils.normalizeFolderMap(folders || {})).length,
69036932
infoCount: Object.keys(info || {}).length
69046933
});
69056934
};
@@ -8814,7 +8843,13 @@ window.setSettingsMode = setSettingsMode;
88148843
initOverflowGuard();
88158844
renderPerformanceDiagnostics();
88168845
await fetchPluginVersion();
8817-
await refreshAll();
8846+
try {
8847+
await refreshAll();
8848+
} catch (error) {
8849+
// Keep initial settings sections visible on first-load API hiccups.
8850+
refreshSettingsUx();
8851+
showError('Initial data load failed', error);
8852+
}
88188853
const serverMode = getServerSettingsMode();
88198854
if (serverMode) {
88208855
settingsUiState.mode = serverMode;
@@ -8841,6 +8876,11 @@ window.setSettingsMode = setSettingsMode;
88418876
}
88428877
settingsUiState.initialized = true;
88438878
} catch (error) {
8879+
try {
8880+
refreshSettingsUx();
8881+
} catch (_ignored) {
8882+
// Best effort only; do not shadow the original initialization error.
8883+
}
88448884
showError('Initialization failed', error);
88458885
}
88468886
})();

tests/settings-bindings.test.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,12 @@ test('basic toolbar actions reuse compact progress overlay for docker and vm flo
7979
assert.match(script, /openImportApplyProgressDialog\(resolvedType, progressTotal\);/);
8080
assert.match(script, /setProgress\(0, 'Creating safety backup\.\.\.'\);/);
8181
});
82+
83+
test('fresh install guard keeps basic Docker/VM sections visible on startup failures', () => {
84+
assert.match(script, /const sectionContainsSelector = \(section, selector\) =>/);
85+
assert.match(script, /return sectionContainsSelector\(section, 'tbody#docker, tbody#vms'\);/);
86+
assert.match(script, /if \(!visibleKeys\.size && settingsUiState\.mode === 'basic' && !settingsUiState\.query\) \{/);
87+
assert.match(script, /for \(const section of getBasicWorkspaceSections\(\)\) \{/);
88+
assert.match(script, /visibleKeys\.add\(section\.key\);/);
89+
assert.match(script, /showError\('Initial data load failed', error\);/);
90+
});

0 commit comments

Comments
 (0)