Skip to content

Commit 5da8e8a

Browse files
Polish wizard smart detect review flow
1 parent ec42713 commit 5da8e8a

7 files changed

Lines changed: 493 additions & 74 deletions

File tree

archive/folderview.plus-2026.03.21.13.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+
e281c9cb1095b35f515ef96a283e870eb31ff4334f471604e55711e46787fdf5 folderview.plus-2026.03.21.42.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.41">
10-
<!ENTITY md5 "f7495fd47195a61f75848f0360a72f29">
9+
<!ENTITY version "2026.03.21.42">
10+
<!ENTITY md5 "d85fda43a50dee3694be719db727fda9">
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.42
17+
- UX: Setup assistant smart-detect now reports confidence, match reasons, and review-needed items instead of silently forcing weak matches into fallback assignment.
18+
- Fix: Expanded Docker and VM alias/heuristic coverage so template detection stays stronger for management, network, gaming, media, and utility workloads.
19+
- Regression guard: Added review-path and VM smart-detect coverage so wizard assignment and starter selection stay aligned before main merge.
20+
21+
1622
###2026.03.21.41
1723
- Fix: Smart template creation in the setup wizard now derives folders from per-item best matches instead of weaker aggregate workload guesses.
1824
- Fix: Improved starter-folder auto-assignment reliability so mixed app stacks create and use the folders they actually need.

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

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4054,7 +4054,7 @@ const STARTER_TEMPLATE_BLUEPRINTS = Object.freeze({
40544054
name: 'Desktop VMs',
40554055
icon: '/plugins/folderview.plus/images/icons/folder-home.svg',
40564056
categories: Object.freeze(['desktop', 'homelab']),
4057-
detect: Object.freeze(['desktop', 'workstation', 'windows', 'win11', 'ubuntu-desktop', 'macos'])
4057+
detect: Object.freeze(['desktop', 'workstation', 'windows', 'win11', 'ubuntu-desktop', 'ubuntu desktop', 'macos', 'fedora-workstation', 'linuxmint'])
40584058
}),
40594059
Object.freeze({
40604060
name: 'Windows VMs',
@@ -4078,37 +4078,37 @@ const STARTER_TEMPLATE_BLUEPRINTS = Object.freeze({
40784078
name: 'Utility VMs',
40794079
icon: '/plugins/folderview.plus/images/icons/folder-tools.svg',
40804080
categories: Object.freeze(['utility', 'minimal', 'homelab']),
4081-
detect: Object.freeze(['utility', 'tools', 'helper', 'management'])
4081+
detect: Object.freeze(['utility', 'tools', 'helper', 'management', 'omv', 'openmediavault', 'truenas'])
40824082
}),
40834083
Object.freeze({
40844084
name: 'Management VMs',
40854085
icon: '/plugins/folderview.plus/images/icons/folder-tools.svg',
40864086
categories: Object.freeze(['utility', 'ops', 'homelab']),
4087-
detect: Object.freeze(['management', 'controller', 'admin', 'jumpbox'])
4087+
detect: Object.freeze(['management', 'controller', 'admin', 'jumpbox', 'proxmox', 'pve', 'hypervisor'])
40884088
}),
40894089
Object.freeze({
40904090
name: 'Infrastructure VMs',
40914091
icon: '/plugins/folderview.plus/images/icons/folder-default.svg',
40924092
categories: Object.freeze(['server', 'network', 'ops', 'homelab']),
4093-
detect: Object.freeze(['infra', 'domain', 'controller', 'gateway', 'dns', 'proxy'])
4093+
detect: Object.freeze(['infra', 'infrastructure', 'domain', 'controller', 'gateway', 'dns', 'proxy', 'unifi', 'k3s', 'k8s'])
40944094
}),
40954095
Object.freeze({
40964096
name: 'Network VMs',
40974097
icon: '/plugins/folderview.plus/images/icons/folder-network.svg',
40984098
categories: Object.freeze(['network', 'homelab']),
4099-
detect: Object.freeze(['router', 'firewall', 'pfsense', 'opnsense', 'network', 'dns', 'proxy'])
4099+
detect: Object.freeze(['router', 'firewall', 'pfsense', 'opnsense', 'vyos', 'network', 'dns', 'proxy', 'unifi'])
41004100
}),
41014101
Object.freeze({
41024102
name: 'Security VMs',
41034103
icon: '/plugins/folderview.plus/images/icons/folder-security.svg',
41044104
categories: Object.freeze(['security', 'ops', 'homelab']),
4105-
detect: Object.freeze(['security', 'siem', 'wazuh', 'ids', 'ips', 'firewall'])
4105+
detect: Object.freeze(['security', 'siem', 'wazuh', 'ids', 'ips', 'firewall', 'sentinel', 'edr'])
41064106
}),
41074107
Object.freeze({
41084108
name: 'Identity VMs',
41094109
icon: '/plugins/folderview.plus/images/icons/folder-security.svg',
41104110
categories: Object.freeze(['security', 'server', 'homelab']),
4111-
detect: Object.freeze(['auth', 'identity', 'ldap', 'ad', 'domain-controller'])
4111+
detect: Object.freeze(['auth', 'identity', 'ldap', 'ad', 'domain-controller', 'freeipa', 'keycloak'])
41124112
}),
41134113
Object.freeze({
41144114
name: 'Backups',
@@ -4144,7 +4144,7 @@ const STARTER_TEMPLATE_BLUEPRINTS = Object.freeze({
41444144
name: 'Cloud Gaming VMs',
41454145
icon: '/plugins/folderview.plus/images/icons/folder-gaming.svg',
41464146
categories: Object.freeze(['gaming', 'desktop', 'homelab']),
4147-
detect: Object.freeze(['cloud-gaming', 'parsec', 'sunshine', 'moonlight', 'gaming'])
4147+
detect: Object.freeze(['cloud-gaming', 'parsec', 'sunshine', 'moonlight', 'gaming', 'steam'])
41484148
})
41494149
])
41504150
});
@@ -4154,6 +4154,27 @@ const STARTER_TEMPLATE_FALLBACK_BY_TYPE = Object.freeze({
41544154
docker: 'Utilities',
41554155
vm: 'Utility VMs'
41564156
});
4157+
const STARTER_TEMPLATE_MATCH_ALIASES = Object.freeze({
4158+
docker: Object.freeze({
4159+
jellyseerr: Object.freeze(['seerr', 'overseerr', 'media', 'request']),
4160+
wizarrrr: Object.freeze(['wizarr', 'media', 'invite']),
4161+
'nginx-proxy-manager': Object.freeze(['reverse proxy', 'proxy', 'npm']),
4162+
cloudflared: Object.freeze(['cloudflare', 'tunnel', 'remote access']),
4163+
homeassistant: Object.freeze(['home assistant', 'automation', 'haos']),
4164+
haos: Object.freeze(['home assistant', 'automation']),
4165+
'code-server': Object.freeze(['development', 'vscode', 'coder']),
4166+
unifi: Object.freeze(['network', 'controller'])
4167+
}),
4168+
vm: Object.freeze({
4169+
pve: Object.freeze(['proxmox', 'management', 'hypervisor']),
4170+
proxmox: Object.freeze(['pve', 'management', 'hypervisor']),
4171+
haos: Object.freeze(['home assistant', 'automation']),
4172+
omv: Object.freeze(['openmediavault', 'utility', 'management']),
4173+
truenas: Object.freeze(['storage', 'server', 'management']),
4174+
unifi: Object.freeze(['network', 'controller']),
4175+
dc: Object.freeze(['domain controller', 'identity', 'infrastructure'])
4176+
})
4177+
});
41574178

41584179
const normalizeStarterTemplateMatchText = (value) => (
41594180
String(value || '')
@@ -4184,11 +4205,23 @@ const buildStarterTemplateHeuristicMap = (type, blueprintName) => {
41844205
return dockerMap[normalizedName] || null;
41854206
}
41864207
const vmMap = {
4187-
'utility-vms': { contains: ['utility', 'tools', 'helper', 'management', 'admin'] },
4188-
'network-vms': { contains: ['router', 'firewall', 'pfsense', 'opnsense', 'dns', 'proxy'] },
4189-
'security-vms': { contains: ['security', 'siem', 'wazuh', 'ids', 'ips', 'firewall'] },
4190-
'desktop-vms': { contains: ['desktop', 'workstation', 'windows', 'ubuntu', 'macos'] },
4191-
'gaming-vms': { contains: ['gaming', 'steam', 'parsec', 'moonlight', 'sunshine', 'gpu'] }
4208+
'production-vms': { contains: ['production', 'prod', 'server', 'srv', 'node'] },
4209+
'desktop-vms': { contains: ['desktop', 'workstation', 'windows', 'ubuntu', 'fedora', 'macos'] },
4210+
'windows-vms': { contains: ['windows', 'win10', 'win11', 'server2019', 'server2022', 'windows-server'] },
4211+
'lab-vms': { contains: ['lab', 'test', 'qa', 'sandbox', 'staging', 'dev'] },
4212+
'dev-test-vms': { contains: ['dev', 'test', 'qa', 'sandbox', 'build'] },
4213+
'utility-vms': { contains: ['utility', 'tools', 'helper', 'management', 'admin', 'openmediavault', 'omv', 'truenas'] },
4214+
'management-vms': { contains: ['management', 'controller', 'admin', 'jumpbox', 'proxmox', 'pve', 'hypervisor'] },
4215+
'infrastructure-vms': { contains: ['infra', 'infrastructure', 'domain', 'controller', 'gateway', 'dns', 'proxy', 'unifi', 'k3s', 'k8s'] },
4216+
'network-vms': { contains: ['router', 'firewall', 'pfsense', 'opnsense', 'vyos', 'dns', 'proxy', 'unifi'] },
4217+
'security-vms': { contains: ['security', 'siem', 'wazuh', 'ids', 'ips', 'firewall', 'sentinel', 'edr'] },
4218+
'identity-vms': { contains: ['security', 'identity', 'auth', 'ldap', 'ad', 'freeipa', 'keycloak', 'domain-controller'] },
4219+
backups: { contains: ['backup', 'vault', 'archive', 'replica'] },
4220+
'recovery-vms': { contains: ['recovery', 'restore', 'disaster', 'snapshot'] },
4221+
'media-vms': { contains: ['media', 'plex', 'jellyfin', 'emby'] },
4222+
'streaming-vms': { contains: ['stream', 'obs', 'media', 'encode', 'transcode'] },
4223+
'gaming-vms': { contains: ['gaming', 'steam', 'parsec', 'moonlight', 'sunshine', 'gpu'] },
4224+
'cloud-gaming-vms': { contains: ['cloud-gaming', 'parsec', 'sunshine', 'moonlight', 'gaming', 'steam'] }
41924225
};
41934226
return vmMap[normalizedName] || null;
41944227
};
@@ -4214,6 +4247,18 @@ const collectStarterTemplateSmartSignals = (type) => {
42144247
tokenSet.add(normalized);
42154248
}
42164249
});
4250+
if (options.expandAliases !== false) {
4251+
const aliasMap = STARTER_TEMPLATE_MATCH_ALIASES[resolvedType] || {};
4252+
Object.entries(aliasMap).forEach(([token, aliases]) => {
4253+
if (!normalizedText.includes(token)) {
4254+
return;
4255+
}
4256+
(Array.isArray(aliases) ? aliases : []).forEach((alias) => addTokens(alias, {
4257+
allowPhrase: false,
4258+
expandAliases: false
4259+
}));
4260+
});
4261+
}
42174262
};
42184263
Object.entries(infoByName).forEach(([itemName, itemInfo]) => {
42194264
addTokens(itemName);

0 commit comments

Comments
 (0)