Skip to content

Commit 9eee9ec

Browse files
Polish wizard apply and completion modal layout
1 parent 5282b9e commit 9eee9ec

6 files changed

Lines changed: 224 additions & 52 deletions

File tree

archive/folderview.plus-2026.03.21.08.txz.sha256

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1006a103f2602889abcae1d4684f74a1f3df91418ddff717290cd93c60092ce8 folderview.plus-2026.03.21.37.txz

folderview.plus.plg

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@
66
<!ENTITY launch "Settings/FolderViewPlus">
77
<!ENTITY plugdir "/usr/local/emhttp/plugins/&name;">
88
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/dev/folderview.plus.plg">
9-
<!ENTITY version "2026.03.21.36">
10-
<!ENTITY md5 "569ac9456eac5448c1484a050aa5f0f7">
9+
<!ENTITY version "2026.03.21.37">
10+
<!ENTITY md5 "c1ac6ff890ddb338756c12b6605a2385">
1111
]>
1212

1313
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;" pluginURL="&pluginURL;" icon="folder-icon.png" support="https://forums.unraid.net/topic/197631-plugin-folderview-plus/" min="7.0.0">
1414
<CHANGES>
1515

16+
###2026.03.21.37
17+
- UX: Tightened setup-apply modal spacing and typography so the summary stays compact and readable without unnecessary vertical gaps.
18+
- UX: Added dedicated wizard modal styling to eliminate desktop scrollbar/overflow artifacts for setup confirmation and completion dialogs.
19+
- UX: Updated the setup-complete screen to the same structured summary layout used by setup apply for consistent visual quality.
20+
21+
1622
###2026.03.21.36
1723
- Fix: Restored wizard template smart-assign execution by exporting `utils.normalizeFolderMembers`, so detected Docker/VM items move into newly created starter folders.
1824
- UX: Redesigned the setup apply confirmation modal with structured summary rows, warning blocks, and clearer apply intent.

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

Lines changed: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,69 +3290,35 @@ const toSetupAssistantDisplayText = (value, fallback = '-') => {
32903290
return text || fallback;
32913291
};
32923292

3293+
const markSetupAssistantSwalModal = () => {
3294+
setTimeout(() => {
3295+
$('.sweet-alert:visible').addClass('fv-setup-swal-modal');
3296+
}, 0);
3297+
};
3298+
32933299
const renderSetupAssistantSwalSummaryHtml = ({
32943300
metaRows = [],
32953301
detailRows = [],
32963302
warningLines = [],
32973303
failureLines = [],
32983304
footerText = ''
32993305
} = {}) => {
3300-
const rowDivider = 'border-bottom:1px solid rgba(148, 170, 196, 0.18);';
33013306
const metaHtml = metaRows.length
3302-
? `
3303-
<div style="display:flex;flex-wrap:wrap;gap:6px 8px;margin-bottom:10px;">
3304-
${metaRows.map((row) => `
3305-
<span style="display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;border:1px solid rgba(148,170,196,0.3);background:rgba(18,28,42,0.72);font-size:12px;line-height:1.2;">
3306-
<strong style="margin-right:5px;">${escapeHtml(row.label)}:</strong>${escapeHtml(row.value)}
3307-
</span>
3308-
`).join('')}
3309-
</div>
3310-
`
3307+
? `<div class="fv-setup-swal-meta">${metaRows.map((row) => `<span class="fv-setup-swal-chip"><strong>${escapeHtml(row.label)}:</strong><span>${escapeHtml(row.value)}</span></span>`).join('')}</div>`
33113308
: '';
33123309
const detailHtml = detailRows.length
3313-
? `
3314-
<div style="border:1px solid rgba(148,170,196,0.28);border-radius:10px;background:rgba(9,16,24,0.74);overflow:hidden;">
3315-
${detailRows.map((row, index) => `
3316-
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:7px 10px;${index < detailRows.length - 1 ? rowDivider : ''}">
3317-
<span style="font-weight:600;color:#d9e5f6;">${escapeHtml(row.label)}</span>
3318-
<span style="text-align:right;color:#eaf2ff;">${escapeHtml(row.value)}</span>
3319-
</div>
3320-
`).join('')}
3321-
</div>
3322-
`
3310+
? `<div class="fv-setup-swal-table">${detailRows.map((row) => `<div class="fv-setup-swal-row"><span class="fv-setup-swal-row-label">${escapeHtml(row.label)}</span><span class="fv-setup-swal-row-value">${escapeHtml(row.value)}</span></div>`).join('')}</div>`
33233311
: '';
33243312
const warningHtml = warningLines.length
3325-
? `
3326-
<div style="margin-top:10px;padding:8px 10px;border-radius:9px;border:1px solid rgba(255,193,94,0.42);background:rgba(255,193,94,0.1);text-align:left;">
3327-
<div style="font-weight:700;margin-bottom:4px;color:#ffd494;">Warnings</div>
3328-
<ul style="margin:0 0 0 18px;padding:0;line-height:1.35;">
3329-
${warningLines.map((line) => `<li style="margin:2px 0;">${escapeHtml(line)}</li>`).join('')}
3330-
</ul>
3331-
</div>
3332-
`
3313+
? `<div class="fv-setup-swal-listbox is-warning"><div class="fv-setup-swal-list-title">Warnings</div><ul class="fv-setup-swal-list">${warningLines.map((line) => `<li>${escapeHtml(line)}</li>`).join('')}</ul></div>`
33333314
: '';
33343315
const failureHtml = failureLines.length
3335-
? `
3336-
<div style="margin-top:10px;padding:8px 10px;border-radius:9px;border:1px solid rgba(255,116,116,0.48);background:rgba(255,116,116,0.1);text-align:left;">
3337-
<div style="font-weight:700;margin-bottom:4px;color:#ffb4b4;">Failed tasks</div>
3338-
<ul style="margin:0 0 0 18px;padding:0;line-height:1.35;">
3339-
${failureLines.map((line) => `<li style="margin:2px 0;">${escapeHtml(line)}</li>`).join('')}
3340-
</ul>
3341-
</div>
3342-
`
3316+
? `<div class="fv-setup-swal-listbox is-failure"><div class="fv-setup-swal-list-title">Failed tasks</div><ul class="fv-setup-swal-list">${failureLines.map((line) => `<li>${escapeHtml(line)}</li>`).join('')}</ul></div>`
33433317
: '';
33443318
const footerHtml = footerText
3345-
? `<div style="margin-top:10px;font-weight:600;color:#d7e6fb;">${escapeHtml(footerText)}</div>`
3319+
? `<div class="fv-setup-swal-footer">${escapeHtml(footerText)}</div>`
33463320
: '';
3347-
return `
3348-
<div style="max-width:560px;margin:0 auto;text-align:left;line-height:1.35;">
3349-
${metaHtml}
3350-
${detailHtml}
3351-
${warningHtml}
3352-
${failureHtml}
3353-
${footerHtml}
3354-
</div>
3355-
`;
3321+
return `<div class="fv-setup-swal">${metaHtml}${detailHtml}${warningHtml}${failureHtml}${footerHtml}</div>`;
33563322
};
33573323

33583324
const buildSetupAssistantVerificationReport = (importOutcomes, templateOutcomes, ruleOutcomes, validationWarnings = []) => {
@@ -3642,12 +3608,14 @@ const confirmSetupAssistantApply = async (impactSummary, reviewValidation) => (
36423608
title: setupAssistantState.dryRunOnly ? 'Run setup dry run?' : 'Apply setup assistant changes?',
36433609
text: html,
36443610
html: true,
3611+
customClass: 'fv-setup-swal-modal',
36453612
type: hasDeletes && setupAssistantState.dryRunOnly !== true ? 'warning' : 'info',
36463613
showCancelButton: true,
36473614
confirmButtonText: setupAssistantState.dryRunOnly ? 'Run dry run' : 'Apply now',
36483615
cancelButtonText: 'Keep editing',
36493616
closeOnConfirm: true
36503617
}, (isConfirm) => resolve(isConfirm === true));
3618+
markSetupAssistantSwalModal();
36513619
})
36523620
);
36533621

@@ -3713,18 +3681,43 @@ const retrySetupAssistantFailures = async (failures = []) => {
37133681
};
37143682
};
37153683

3716-
const confirmSetupAssistantUndo = async (summaryText, canUndo = true) => (
3684+
const confirmSetupAssistantUndo = async (summaryPayload, canUndo = true) => (
37173685
new Promise((resolve) => {
3686+
const html = typeof summaryPayload === 'string'
3687+
? renderSetupAssistantSwalSummaryHtml({
3688+
detailRows: summaryPayload
3689+
.split('\n')
3690+
.map((line) => String(line || '').trim())
3691+
.filter(Boolean)
3692+
.map((line) => {
3693+
const separator = line.indexOf(':');
3694+
if (separator <= 0) {
3695+
return { label: 'Info', value: line };
3696+
}
3697+
return {
3698+
label: line.slice(0, separator).trim(),
3699+
value: line.slice(separator + 1).trim()
3700+
};
3701+
}),
3702+
footerText: canUndo ? 'Undo this setup now?' : ''
3703+
})
3704+
: renderSetupAssistantSwalSummaryHtml({
3705+
...(summaryPayload && typeof summaryPayload === 'object' ? summaryPayload : {}),
3706+
footerText: canUndo ? 'Undo this setup now?' : ''
3707+
});
37183708
swal({
37193709
title: 'Setup assistant complete',
3720-
text: canUndo ? `${summaryText}\n\nUndo this setup now?` : summaryText,
3710+
text: html,
3711+
html: true,
3712+
customClass: 'fv-setup-swal-modal',
37213713
type: 'success',
37223714
showCancelButton: canUndo === true,
37233715
confirmButtonText: canUndo ? 'Undo setup' : 'Done',
37243716
cancelButtonText: 'Done',
37253717
showLoaderOnConfirm: canUndo === true,
37263718
closeOnConfirm: canUndo !== true
37273719
}, (undoNow) => resolve(undoNow === true));
3720+
markSetupAssistantSwalModal();
37283721
})
37293722
);
37303723

@@ -4022,13 +4015,15 @@ const applySetupAssistantPlan = async () => {
40224015
title: 'Setup applied with partial failures',
40234016
text: failureSummaryHtml,
40244017
html: true,
4018+
customClass: 'fv-setup-swal-modal',
40254019
type: 'warning',
40264020
showCancelButton: true,
40274021
confirmButtonText: 'Retry failures',
40284022
cancelButtonText: 'Done',
40294023
showLoaderOnConfirm: true,
40304024
closeOnConfirm: false
40314025
}, (isConfirm) => resolve(isConfirm === true));
4026+
markSetupAssistantSwalModal();
40324027
});
40334028

40344029
if (retryNow) {
@@ -4051,7 +4046,29 @@ const applySetupAssistantPlan = async () => {
40514046
return;
40524047
}
40534048

4054-
const undoNow = await confirmSetupAssistantUndo(summaryLines.join('\n'), rollbackCreated);
4049+
const undoNow = await confirmSetupAssistantUndo({
4050+
metaRows: [
4051+
{ label: 'Mode', value: toSetupAssistantDisplayText(setupAssistantState.mode) },
4052+
{ label: 'Route', value: toSetupAssistantDisplayText(setupAssistantState.route) },
4053+
{ label: 'Wizard detail', value: normalizeSetupAssistantExperienceMode(setupAssistantState.experienceMode) },
4054+
{ label: 'Safety mode', value: safetyMode }
4055+
],
4056+
detailRows: [
4057+
{ label: 'Profile defaults', value: setupAssistantState.applyProfileDefaults ? setupAssistantState.profile : 'not applied' },
4058+
{ label: 'Environment defaults', value: setupAssistantState.applyEnvironmentDefaults ? (SETUP_ASSISTANT_ENV_PRESETS[setupAssistantState.environmentPreset]?.label || 'Home Lab') : 'not applied' },
4059+
{ label: 'Docker imports', value: `${importOutcomes.docker}` },
4060+
{ label: 'VM imports', value: `${importOutcomes.vm}` },
4061+
{ label: 'Docker starter folders', value: `${templateOutcomes.docker.created} created, ${templateOutcomes.docker.skippedExisting} skipped, ${Number(templateOutcomes.docker.assignment?.matched) || 0} auto-assigned` },
4062+
{ label: 'VM starter folders', value: `${templateOutcomes.vm.created} created, ${templateOutcomes.vm.skippedExisting} skipped, ${Number(templateOutcomes.vm.assignment?.matched) || 0} auto-assigned` },
4063+
{ label: 'Docker starter rules', value: `${ruleOutcomes.docker.created} added` },
4064+
{ label: 'VM starter rules', value: `${ruleOutcomes.vm.created} added` },
4065+
{ label: 'Preference changes', value: `${impactSummary.prefs.totalChanges}` },
4066+
{ label: 'Verification', value: `${verification.passed}/${verification.total} checks passed` },
4067+
{ label: 'Rollback checkpoint', value: setupAssistantState.rollbackCheckpointName || (rollbackCreated ? 'created' : 'skipped (Fast mode)') },
4068+
{ label: 'Duration', value: `${durationSeconds}s` }
4069+
],
4070+
warningLines: validationWarnings.slice(0, 5)
4071+
}, rollbackCreated);
40554072
if (!undoNow || !rollbackCreated) {
40564073
return;
40574074
}

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folderviewplus.css

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5839,6 +5839,131 @@ tr.is-long-press-active {
58395839
}
58405840
}
58415841

5842+
.sweet-alert.fv-setup-swal-modal {
5843+
width: min(560px, calc(100vw - 24px)) !important;
5844+
max-width: min(560px, calc(100vw - 24px)) !important;
5845+
padding: 12px 14px 10px !important;
5846+
overflow: visible !important;
5847+
}
5848+
5849+
.sweet-alert.fv-setup-swal-modal .sa-icon {
5850+
margin: 4px auto 8px !important;
5851+
width: 68px !important;
5852+
height: 68px !important;
5853+
}
5854+
5855+
.sweet-alert.fv-setup-swal-modal h2 {
5856+
margin: 4px 0 6px !important;
5857+
font-size: 1.9rem !important;
5858+
line-height: 1.2 !important;
5859+
}
5860+
5861+
.sweet-alert.fv-setup-swal-modal p {
5862+
margin: 0 !important;
5863+
padding: 0 !important;
5864+
max-height: none !important;
5865+
overflow: visible !important;
5866+
line-height: 1.2 !important;
5867+
}
5868+
5869+
.sweet-alert.fv-setup-swal-modal .sa-button-container {
5870+
margin-top: 10px !important;
5871+
}
5872+
5873+
.fv-setup-swal {
5874+
max-width: 520px;
5875+
margin: 0 auto;
5876+
text-align: left;
5877+
line-height: 1.24;
5878+
}
5879+
5880+
.fv-setup-swal-meta {
5881+
display: flex;
5882+
flex-wrap: wrap;
5883+
gap: 6px;
5884+
margin-bottom: 8px;
5885+
}
5886+
5887+
.fv-setup-swal-chip {
5888+
display: inline-flex;
5889+
align-items: center;
5890+
gap: 4px;
5891+
padding: 3px 8px;
5892+
border-radius: 999px;
5893+
border: 1px solid rgba(148, 170, 196, 0.34);
5894+
background: rgba(18, 28, 42, 0.72);
5895+
font-size: 12px;
5896+
}
5897+
5898+
.fv-setup-swal-table {
5899+
border: 1px solid rgba(148, 170, 196, 0.28);
5900+
border-radius: 10px;
5901+
background: rgba(9, 16, 24, 0.78);
5902+
overflow: hidden;
5903+
}
5904+
5905+
.fv-setup-swal-row {
5906+
display: flex;
5907+
align-items: flex-start;
5908+
justify-content: space-between;
5909+
gap: 10px;
5910+
padding: 6px 10px;
5911+
font-size: 1.03rem;
5912+
}
5913+
5914+
.fv-setup-swal-row + .fv-setup-swal-row {
5915+
border-top: 1px solid rgba(148, 170, 196, 0.18);
5916+
}
5917+
5918+
.fv-setup-swal-row-label {
5919+
font-weight: 600;
5920+
color: #d9e5f6;
5921+
}
5922+
5923+
.fv-setup-swal-row-value {
5924+
text-align: right;
5925+
color: #eaf2ff;
5926+
}
5927+
5928+
.fv-setup-swal-listbox {
5929+
margin-top: 8px;
5930+
padding: 7px 10px;
5931+
border-radius: 9px;
5932+
border: 1px solid rgba(148, 170, 196, 0.35);
5933+
background: rgba(148, 170, 196, 0.1);
5934+
}
5935+
5936+
.fv-setup-swal-listbox.is-warning {
5937+
border-color: rgba(255, 193, 94, 0.44);
5938+
background: rgba(255, 193, 94, 0.11);
5939+
}
5940+
5941+
.fv-setup-swal-listbox.is-failure {
5942+
border-color: rgba(255, 116, 116, 0.5);
5943+
background: rgba(255, 116, 116, 0.12);
5944+
}
5945+
5946+
.fv-setup-swal-list-title {
5947+
margin-bottom: 4px;
5948+
font-weight: 700;
5949+
}
5950+
5951+
.fv-setup-swal-list {
5952+
margin: 0;
5953+
padding-left: 18px;
5954+
}
5955+
5956+
.fv-setup-swal-list li {
5957+
margin: 2px 0;
5958+
}
5959+
5960+
.fv-setup-swal-footer {
5961+
margin-top: 8px;
5962+
text-align: center;
5963+
font-weight: 600;
5964+
color: #d7e6fb;
5965+
}
5966+
58425967
@media (max-width: 760px) {
58435968
:root {
58445969
--fv-unraid-bottom-bar-offset: 40px;
@@ -5880,6 +6005,30 @@ tr.is-long-press-active {
58806005
padding: 0.16rem 0.38rem;
58816006
}
58826007

6008+
.sweet-alert.fv-setup-swal-modal {
6009+
left: calc(env(safe-area-inset-left) + 0.5rem) !important;
6010+
right: calc(env(safe-area-inset-right) + 0.5rem) !important;
6011+
top: calc(env(safe-area-inset-top) + 0.5rem) !important;
6012+
width: auto !important;
6013+
max-width: none !important;
6014+
max-height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 1rem) !important;
6015+
overflow-y: auto !important;
6016+
-webkit-overflow-scrolling: touch;
6017+
}
6018+
6019+
.sweet-alert.fv-setup-swal-modal h2 {
6020+
font-size: 1.4rem !important;
6021+
}
6022+
6023+
.sweet-alert.fv-setup-swal-modal .fv-setup-swal-row {
6024+
flex-direction: column;
6025+
gap: 2px;
6026+
}
6027+
6028+
.sweet-alert.fv-setup-swal-modal .fv-setup-swal-row-value {
6029+
text-align: left;
6030+
}
6031+
58836032
#fv-settings-action-bar {
58846033
left: 0.5rem;
58856034
width: auto;

0 commit comments

Comments
 (0)