/**
* Advanced model selector and model-widget path helpers.
*/
import { translate } from './locales.js';
import { escapeHtml } from './safe_dom.js';
const t = (key, params) => translate(key, params);
export function _openAdvancedModelSelector(initialSelectedMap, onConfirm) {
let localSelection = new Map();
for (const [k, v] of initialSelectedMap.entries()) {
localSelection.set(k, new Set(v));
}
const getFolderSelectionCount = (folderKey) => {
let nKey = folderKey;
const parts = folderKey.split('|');
if (parts.length >= 3 && parts[2].startsWith('/')) {
parts[2] = parts[2].substring(1);
nKey = parts.join('|');
}
return localSelection.has(nKey) ? localSelection.get(nKey).size : 0;
};
const modal = document.createElement('div');
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100vw';
modal.style.height = '100vh';
modal.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
modal.style.zIndex = '9999999';
modal.style.display = 'flex';
modal.style.justifyContent = 'center';
modal.style.alignItems = 'center';
modal.style.fontFamily = 'Roboto, Inter, sans-serif';
const contentDiv = document.createElement('div');
contentDiv.style.background = 'var(--amb-bg-card)';
contentDiv.style.borderRadius = '8px';
contentDiv.style.width = '92vw';
contentDiv.style.height = '88vh';
contentDiv.style.display = 'flex';
contentDiv.style.flexDirection = 'column';
contentDiv.style.boxShadow = '0 24px 38px 3px rgba(0,0,0,0.4), 0 9px 46px 8px rgba(0,0,0,0.3), 0 11px 15px -7px rgba(0,0,0,0.5)';
contentDiv.style.overflow = 'hidden';
// Header
const header = document.createElement('div');
header.style.padding = '16px 24px';
header.style.borderBottom = '1px solid var(--amb-border-strong)';
header.style.display = 'flex';
header.style.justifyContent = 'space-between';
header.style.alignItems = 'center';
const titleContainer = document.createElement('div');
titleContainer.style.display = 'flex';
titleContainer.style.alignItems = 'center';
titleContainer.style.gap = '12px';
const title = document.createElement('h2');
title.textContent = t('detailSelectModels');
title.style.margin = '0';
title.style.color = '#e8eaed';
title.style.fontSize = '20px';
title.style.fontWeight = '500';
titleContainer.appendChild(title);
const closeBtn = document.createElement('button');
closeBtn.innerHTML = '✕';
closeBtn.style.background = 'transparent';
closeBtn.style.color = '#9aa0a6';
closeBtn.style.border = 'none';
closeBtn.style.fontSize = '18px';
closeBtn.style.cursor = 'pointer';
closeBtn.onclick = () => document.body.removeChild(modal);
header.appendChild(titleContainer);
header.appendChild(closeBtn);
contentDiv.appendChild(header);
// Body area
const body = document.createElement('div');
body.style.display = 'flex';
body.style.flex = '1';
body.style.overflow = 'hidden';
// Left Panel
const leftPanel = document.createElement('div');
leftPanel.style.width = '280px';
leftPanel.style.borderRight = '1px solid var(--amb-border-strong)';
leftPanel.style.display = 'flex';
leftPanel.style.flexDirection = 'column';
leftPanel.style.background = 'var(--amb-bg-panel)';
const leftTitle = document.createElement('div');
leftTitle.textContent = t('detailFolders');
leftTitle.style.padding = '16px 24px';
leftTitle.style.fontWeight = '500';
leftTitle.style.color = '#9aa0a6';
leftTitle.style.fontSize = '14px';
leftTitle.style.letterSpacing = '0.5px';
leftPanel.appendChild(leftTitle);
const treeContainer = document.createElement('div');
treeContainer.style.flex = '1';
treeContainer.style.overflowY = 'auto';
treeContainer.style.padding = '0 12px 12px 12px';
leftPanel.appendChild(treeContainer);
// Right Panel
const rightPanel = document.createElement('div');
rightPanel.style.flex = '1';
rightPanel.style.display = 'flex';
rightPanel.style.flexDirection = 'column';
rightPanel.style.background = 'var(--amb-bg-card)';
// Toolbar
const toolbar = document.createElement('div');
toolbar.style.padding = '12px 24px';
toolbar.style.borderBottom = '1px solid var(--amb-border-strong)';
toolbar.style.display = 'flex';
toolbar.style.gap = '12px';
toolbar.style.alignItems = 'center';
toolbar.style.flexWrap = 'wrap';
const createTBtn = (text, onClick, primary = false) => {
const b = document.createElement('button');
b.innerText = text;
b.style.padding = '6px 16px';
b.style.background = primary ? 'rgba(255, 255, 255, 0.12)' : 'rgba(255,255,255,0.04)';
b.style.color = primary ? '#ffffff' : '#e8eaed';
b.style.border = primary ? '1px solid rgba(255, 255, 255, 0.25)' : '1px solid #5f6368';
b.style.borderRadius = '4px';
b.style.cursor = 'pointer';
b.style.fontSize = '14px';
b.style.fontWeight = '500';
b.style.transition = 'background-color 0.15s, box-shadow 0.15s';
b.onmouseover = () => {
b.style.background = primary ? 'rgba(138,180,248,0.25)' : 'rgba(255,255,255,0.1)';
};
b.onmouseout = () => {
b.style.background = primary ? 'rgba(138,180,248,0.15)' : 'rgba(255,255,255,0.04)';
};
b.onclick = onClick;
return b;
};
let currentModels = [];
let currentTotal = 0;
let currentFolderKey = 'ALL';
let currentBaseUrl = '';
let currentPage = 1;
let isLoading = false;
const updateSummaryAndBadges = () => {
let total = 0;
for (const set of localSelection.values()) total += set.size;
if (total > 0) {
const countLabel = document.createElement('span');
countLabel.textContent = total;
countLabel.style.color = '#e5e7eb';
countLabel.style.fontSize = '16px';
countLabel.style.fontWeight = '500';
summaryText.replaceChildren(
document.createTextNode(t('detailSelectedItemsPrefix')),
countLabel,
document.createTextNode(t('detailSelectedItemsSuffix')),
);
} else {
summaryText.textContent = t('detailNothingSelected');
}
treeContainer.querySelectorAll('.folder-item').forEach(fBtn => {
const fKey = fBtn.dataset.folderKey;
const count = getFolderSelectionCount(fKey);
const badge = fBtn.querySelector('.selection-badge');
if (badge) {
if (count > 0) {
badge.innerText = count;
badge.style.display = 'inline-flex';
} else {
badge.style.display = 'none';
}
}
});
// Also update total models text in grid
const statusDiv = grid.querySelector('.status-text');
if (statusDiv && !isLoading) {
statusDiv.textContent = t('detailLoadedModels', { loaded: currentModels.length, total: currentTotal });
}
};
const updateGridCheckboxes = () => {
grid.querySelectorAll('.model-card').forEach(card => {
const filename = card.dataset.filename;
const mType = card.dataset.mtype;
const mPathIdx = card.dataset.mpathidx;
const mSubfolder = card.dataset.msubfolder;
const fKey = `${mType}|${mPathIdx}|${mSubfolder}`;
const set = localSelection.get(fKey) || new Set();
const isChecked = set.has(filename);
const cbWrapper = card.querySelector('.cb-wrapper');
const cb = card.querySelector('input[type="checkbox"]');
cb.checked = isChecked;
if (isChecked) {
card.style.border = '2px solid #ffffff';
card.style.background = 'rgba(255, 255, 255, 0.1)';
cbWrapper.style.background = '#ffffff';
cbWrapper.style.border = '2px solid #ffffff';
cbWrapper.querySelector('span').style.color = '#202124';
} else {
card.style.border = '1px solid var(--amb-border-strong)';
card.style.background = 'var(--amb-bg-card-hover)';
cbWrapper.style.background = 'rgba(0,0,0,0.3)';
cbWrapper.style.border = '2px solid #5f6368';
cbWrapper.querySelector('span').style.color = 'transparent';
}
});
updateSummaryAndBadges();
};
const handleBatchSelect = async (action) => {
if (action === 'none') {
if (currentFolderKey === 'ALL') {
localSelection.clear();
} else {
let nCurKey = currentFolderKey;
const cParts = currentFolderKey.split('|');
if (cParts.length >= 3 && cParts[2].startsWith('/')) {
cParts[2] = cParts[2].substring(1);
nCurKey = cParts.join('|');
}
localSelection.delete(nCurKey);
}
updateGridCheckboxes();
return;
}
summaryText.textContent = t('detailComputing');
try {
const res = await fetch(`/anomalous/batch_select?folderKey=${encodeURIComponent(currentFolderKey)}&action=${action}`);
if (!res.ok) throw new Error('API failed');
const data = await res.json();
for (const [fKey, files] of Object.entries(data.selected)) {
let normalizedKey = fKey;
const parts = fKey.split('|');
if (parts.length >= 3 && parts[2].startsWith('/')) {
parts[2] = parts[2].substring(1);
normalizedKey = parts.join('|');
}
const set = localSelection.get(normalizedKey) || new Set();
files.forEach(f => set.add(f));
localSelection.set(normalizedKey, set);
}
updateGridCheckboxes();
} catch (e) {
console.error(e);
summaryText.textContent = t('detailUploadError') + e.message;
setTimeout(() => updateGridCheckboxes(), 2000);
}
};
toolbar.appendChild(createTBtn(t('detailSelectNoPreview'), () => handleBatchSelect('no_preview'), true));
toolbar.appendChild(createTBtn(t('detailSelectNoDesc'), () => handleBatchSelect('no_desc'), true));
const divi = document.createElement('div');
divi.style.width = '1px'; divi.style.height = '24px'; divi.style.background = 'var(--amb-bg-card-hover)'; divi.style.margin = '0 8px';
toolbar.appendChild(divi);
toolbar.appendChild(createTBtn(t('detailSelectAll'), () => handleBatchSelect('all')));
toolbar.appendChild(createTBtn(t('detailClear'), () => handleBatchSelect('none')));
const summaryText = document.createElement('div');
summaryText.style.marginLeft = 'auto';
summaryText.style.color = '#9aa0a6';
summaryText.style.fontWeight = '400';
toolbar.appendChild(summaryText);
rightPanel.appendChild(toolbar);
const grid = document.createElement('div');
grid.style.flex = '1';
grid.style.overflowY = 'auto';
grid.style.padding = '24px';
grid.style.display = 'grid';
grid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(180px, 1fr))';
grid.style.gridAutoRows = 'max-content';
grid.style.gap = '20px';
grid.style.alignContent = 'start';
grid.style.background = 'var(--amb-bg-panel)';
rightPanel.appendChild(grid);
body.appendChild(leftPanel);
body.appendChild(rightPanel);
contentDiv.appendChild(body);
// Footer (Confirm)
const footer = document.createElement('div');
footer.style.padding = '12px 24px';
footer.style.borderTop = '1px solid var(--amb-border-strong)';
footer.style.display = 'flex';
footer.style.justifyContent = 'flex-end';
footer.style.gap = '12px';
const cancelBtn = document.createElement('button');
cancelBtn.textContent = t('detailCancel');
cancelBtn.style.padding = '8px 24px';
cancelBtn.style.background = 'transparent';
cancelBtn.style.color = '#9ca3af';
cancelBtn.style.border = 'none';
cancelBtn.style.borderRadius = '4px';
cancelBtn.style.cursor = 'pointer';
cancelBtn.style.fontWeight = '500';
cancelBtn.style.fontSize = '14px';
cancelBtn.onmouseover = () => cancelBtn.style.background = 'rgba(255, 255, 255, 0.08)';
cancelBtn.onmouseout = () => cancelBtn.style.background = 'transparent';
cancelBtn.onclick = () => document.body.removeChild(modal);
const confirmBtn = document.createElement('button');
confirmBtn.textContent = t('detailConfirm');
confirmBtn.style.padding = '8px 24px';
confirmBtn.style.background = '#e5e7eb';
confirmBtn.style.color = '#111827'; // dark text on bright accent button
confirmBtn.style.border = 'none';
confirmBtn.style.borderRadius = '4px';
confirmBtn.style.cursor = 'pointer';
confirmBtn.style.fontWeight = '600';
confirmBtn.style.fontSize = '14px';
confirmBtn.style.boxShadow = '0 1px 2px 0 rgba(0,0,0,.3), 0 1px 3px 1px rgba(0,0,0,.15)';
confirmBtn.onmouseover = () => { confirmBtn.style.background = '#ffffff'; confirmBtn.style.boxShadow = '0 1px 3px 0 rgba(0,0,0,.3), 0 4px 8px 3px rgba(0,0,0,.15)'; };
confirmBtn.onmouseout = () => { confirmBtn.style.background = '#e5e7eb'; confirmBtn.style.boxShadow = '0 1px 2px 0 rgba(0,0,0,.3), 0 1px 3px 1px rgba(0,0,0,.15)'; };
confirmBtn.onclick = () => {
onConfirm(localSelection);
document.body.removeChild(modal);
};
footer.appendChild(cancelBtn);
footer.appendChild(confirmBtn);
contentDiv.appendChild(footer);
modal.appendChild(contentDiv);
document.body.appendChild(modal);
// Infinite Scroll Logic
const renderCards = (modelsToRender) => {
const frag = document.createDocumentFragment();
modelsToRender.forEach(m => {
const card = document.createElement('div');
card.className = 'model-card';
card.dataset.filename = m.filename;
card.dataset.mtype = m.type;
card.dataset.mpathidx = m.path_idx;
card.dataset.msubfolder = m.subfolder;
card.style.background = 'var(--amb-bg-card-hover)';
card.style.borderRadius = '8px';
card.style.overflow = 'hidden';
card.style.position = 'relative';
card.style.border = '1px solid var(--amb-border-strong)';
card.style.display = 'flex';
card.style.flexDirection = 'column';
card.style.aspectRatio = '2 / 3';
card.style.height = 'auto';
card.style.cursor = 'pointer';
card.style.boxShadow = '0 1px 2px 0 rgba(0,0,0,.3), 0 1px 3px 1px rgba(0,0,0,.15)';
const imgContainer = document.createElement('div');
imgContainer.style.flex = '1';
imgContainer.style.background = 'var(--amb-bg-card)';
imgContainer.style.display = 'flex';
imgContainer.style.justifyContent = 'center';
imgContainer.style.alignItems = 'center';
imgContainer.style.overflow = 'hidden';
if (m.preview_url) {
const isVid = m.preview_url.match(/\.(mp4|webm|mov|avi)(?:&|$)/i);
if (isVid) {
const video = document.createElement('video');
video.src = m.preview_url;
video.style.width = '100%';
video.style.height = '100%';
video.style.objectFit = 'cover';
video.muted = true;
video.loop = true;
video.playsInline = true;
card.addEventListener('mouseenter', () => video.play().catch(e => {}));
card.addEventListener('mouseleave', () => { video.pause(); video.currentTime = 0; });
imgContainer.appendChild(video);
} else {
const img = document.createElement('img');
img.src = m.preview_url;
img.style.width = '100%';
img.style.height = '100%';
img.style.objectFit = 'cover';
imgContainer.appendChild(img);
}
} else {
imgContainer.innerHTML = '';
}
card.appendChild(imgContainer);
const nameBar = document.createElement('div');
nameBar.style.padding = '12px';
nameBar.style.background = 'var(--amb-bg-card-hover)';
nameBar.style.fontSize = '13px';
nameBar.style.color = '#e8eaed';
nameBar.style.whiteSpace = 'nowrap';
nameBar.style.overflow = 'hidden';
nameBar.style.textOverflow = 'ellipsis';
nameBar.style.borderTop = '1px solid var(--amb-border-strong)';
nameBar.innerText = m.filename;
card.appendChild(nameBar);
const cbWrapper = document.createElement('div');
cbWrapper.className = 'cb-wrapper';
cbWrapper.style.position = 'absolute';
cbWrapper.style.top = '8px';
cbWrapper.style.right = '8px';
cbWrapper.style.width = '20px';
cbWrapper.style.height = '20px';
cbWrapper.style.borderRadius = '50%';
cbWrapper.style.background = 'rgba(0,0,0,0.3)';
cbWrapper.style.border = '2px solid #5f6368';
cbWrapper.style.display = 'flex';
cbWrapper.style.justifyContent = 'center';
cbWrapper.style.alignItems = 'center';
cbWrapper.style.pointerEvents = 'none';
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.style.display = 'none';
const checkIcon = document.createElement('span');
checkIcon.innerHTML = '✓';
checkIcon.style.color = 'transparent';
checkIcon.style.fontSize = '12px';
checkIcon.style.fontWeight = 'bold';
cbWrapper.appendChild(checkIcon);
cbWrapper.appendChild(cb);
card.appendChild(cbWrapper);
card.onclick = () => {
const mKey = `${m.type}|${m.path_idx}|${m.subfolder}`;
const cset = localSelection.get(mKey) || new Set();
if (!cset.has(m.filename)) {
cset.add(m.filename);
} else {
cset.delete(m.filename);
}
localSelection.set(mKey, cset);
updateGridCheckboxes();
};
frag.appendChild(card);
});
return frag;
};
const fetchModelsPage = async (isLoadMore = false) => {
if (isLoading) return;
isLoading = true;
const prevStatus = grid.querySelector('.status-text');
if (prevStatus) grid.removeChild(prevStatus);
const loadingIndicator = document.createElement('div');
loadingIndicator.className = 'status-text';
loadingIndicator.style.gridColumn = '1 / -1';
loadingIndicator.style.padding = '40px';
loadingIndicator.style.textAlign = 'center';
loadingIndicator.style.color = '#9aa0a6';
loadingIndicator.style.fontSize = '16px';
loadingIndicator.textContent = t('detailLoading');
grid.appendChild(loadingIndicator);
try {
const sep = currentBaseUrl.includes('?') ? '&' : '?';
const url = `${currentBaseUrl}${sep}page=${currentPage}&limit=50`;
const res = await fetch(url);
if (!res.ok) {
const apiError = document.createElement('div');
apiError.className = 'status-text';
apiError.style.gridColumn = '1 / -1';
apiError.style.padding = '60px';
apiError.style.textAlign = 'center';
apiError.style.color = '#f28b82';
apiError.style.fontSize = '16px';
apiError.style.whiteSpace = 'pre-line';
apiError.textContent = t('detailApiRestart');
grid.replaceChildren(apiError);
isLoading = false;
return;
}
const data = await res.json();
const fetchedModels = data.models || [];
currentTotal = data.total || 0;
if (isLoadMore) {
currentModels = currentModels.concat(fetchedModels);
} else {
currentModels = fetchedModels;
// Keep the loading indicator temporarily, we clear grid except it
Array.from(grid.children).forEach(c => {
if (c !== loadingIndicator) grid.removeChild(c);
});
}
grid.removeChild(loadingIndicator);
if (currentModels.length === 0 && !isLoadMore) {
const emptyModels = document.createElement('div');
emptyModels.className = 'status-text';
emptyModels.style.gridColumn = '1 / -1';
emptyModels.style.padding = '60px';
emptyModels.style.textAlign = 'center';
emptyModels.style.color = '#9aa0a6';
emptyModels.style.fontSize = '16px';
emptyModels.textContent = t('detailNoModels');
grid.replaceChildren(emptyModels);
} else {
grid.appendChild(renderCards(fetchedModels));
updateGridCheckboxes();
// Show status at bottom
const statusText = document.createElement('div');
statusText.className = 'status-text';
statusText.style.gridColumn = '1 / -1';
statusText.style.padding = '20px';
statusText.style.textAlign = 'center';
statusText.style.color = '#5f6368';
statusText.style.fontSize = '14px';
if (currentModels.length >= currentTotal) {
statusText.textContent = t('detailAllLoaded');
} else {
statusText.textContent = t('detailScrollMore');
}
grid.appendChild(statusText);
}
} catch (e) {
console.error(e);
grid.removeChild(loadingIndicator);
}
isLoading = false;
};
grid.onscroll = () => {
if (isLoading || currentModels.length >= currentTotal) return;
// Near bottom detection
if (grid.scrollTop + grid.clientHeight >= grid.scrollHeight - 150) {
currentPage++;
fetchModelsPage(true);
}
};
// Render Folders
const renderFolders = async () => {
treeContainer.innerHTML = '';
const allBtn = document.createElement('div');
allBtn.className = 'folder-item';
allBtn.dataset.folderKey = 'ALL';
allBtn.style.padding = '10px 16px';
allBtn.style.cursor = 'pointer';
allBtn.style.color = '#e8eaed';
allBtn.style.fontSize = '14px';
allBtn.style.fontWeight = '500';
allBtn.style.borderRadius = '0 16px 16px 0';
allBtn.style.marginBottom = '8px';
allBtn.style.display = 'flex';
allBtn.style.justifyContent = 'space-between';
allBtn.style.alignItems = 'center';
allBtn.textContent = `🌟 ${t('models')}`;
let loadFolder = async (fBtn, folderKey, fetchUrl) => {
treeContainer.querySelectorAll('.folder-item').forEach(d => {
d.style.background = 'transparent';
d.style.color = '#9aa0a6';
d.style.fontWeight = '400';
});
fBtn.style.background = 'rgba(255, 255, 255, 0.1)';
fBtn.style.color = '#ffffff';
fBtn.style.fontWeight = '500';
currentFolderKey = folderKey;
currentBaseUrl = fetchUrl;
currentPage = 1;
currentModels = [];
currentTotal = 0;
grid.innerHTML = '';
await fetchModelsPage(false);
};
allBtn.onclick = () => loadFolder(allBtn, 'ALL', '/anomalous/all_scan_models');
treeContainer.appendChild(allBtn);
if (!this.foldersData) {
const res = await fetch('/anomalous/folders');
const data = await res.json();
this.foldersData = data.folders || [];
}
this.foldersData.forEach(tData => {
const typeItem = document.createElement('div');
typeItem.style.marginBottom = '4px';
const tTitle = document.createElement('div');
tTitle.innerText = tData.label;
tTitle.style.color = '#9aa0a6';
tTitle.style.fontWeight = '500';
tTitle.style.padding = '8px 16px';
tTitle.style.fontSize = '12px';
tTitle.style.marginTop = '8px';
typeItem.appendChild(tTitle);
for (const [path, fData] of Object.entries(tData.folders)) {
if (fData.model_count === 0 && !fData.has_models) continue;
const folderKey = `${tData.type}|${tData.path_idx}|${path}`;
const fBtn = document.createElement('div');
fBtn.className = 'folder-item';
fBtn.dataset.folderKey = folderKey;
const depth = path.split('/').length - 1;
fBtn.style.padding = `8px 16px 8px ${16 + depth * 16}px`;
fBtn.style.cursor = 'pointer';
fBtn.style.color = '#9aa0a6';
fBtn.style.fontSize = '14px';
fBtn.style.borderRadius = '0 16px 16px 0';
fBtn.style.display = 'flex';
fBtn.style.justifyContent = 'space-between';
fBtn.style.alignItems = 'center';
const leftPart = document.createElement('div');
leftPart.style.display = 'flex';
leftPart.style.alignItems = 'center';
leftPart.style.gap = '8px';
leftPart.innerHTML = ` ${escapeHtml(fData.name)} (${escapeHtml(fData.model_count)})`;
const badge = document.createElement('div');
badge.className = 'selection-badge';
badge.style.background = '#e5e7eb';
badge.style.color = '#111827';
badge.style.fontSize = '11px';
badge.style.fontWeight = '500';
badge.style.padding = '1px 6px';
badge.style.borderRadius = '10px';
badge.style.display = 'none';
fBtn.appendChild(leftPart);
fBtn.appendChild(badge);
fBtn.onmouseover = () => { if (currentFolderKey !== folderKey) fBtn.style.background = 'rgba(255,255,255,0.04)'; };
fBtn.onmouseout = () => { if (currentFolderKey !== folderKey) fBtn.style.background = 'transparent'; };
fBtn.onclick = () => {
const params = new URLSearchParams({ type: tData.type, path_idx: tData.path_idx, subfolder: path });
loadFolder(fBtn, folderKey, '/anomalous/models?' + params.toString());
};
typeItem.appendChild(fBtn);
}
treeContainer.appendChild(typeItem);
});
allBtn.onclick();
};
renderFolders();
}
export function setWidgetValuePath(node, relPath) {
if (!node.widgets || node.widgets.length === 0) return;
const w = node.widgets.find(wg => wg.type === 'combo');
const targetWidget = w || node.widgets[0];
if (!targetWidget) return;
if (targetWidget.options && targetWidget.options.values) {
const normalizedTarget = relPath.replace(/\\/g, '/');
const match = targetWidget.options.values.find(v => {
return String(v).replace(/\\/g, '/') === normalizedTarget;
});
if (match) {
targetWidget.value = match;
return;
}
}
targetWidget.value = relPath;
}