mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
fix(sidebar): escape folder names and paths to support double quotes
- Import and use escapeHtml and escapeAttribute in SidebarManager.js - Escape data-path and title attributes in folder tree and breadcrumbs - Use CSS.escape() for attribute selectors in updateTreeSelection - Fixes issue #843 where folders with double quotes broke navigation
This commit is contained in:
@@ -7,6 +7,7 @@ import { translate } from '../utils/i18nHelpers.js';
|
|||||||
import { state } from '../state/index.js';
|
import { state } from '../state/index.js';
|
||||||
import { bulkManager } from '../managers/BulkManager.js';
|
import { bulkManager } from '../managers/BulkManager.js';
|
||||||
import { showToast } from '../utils/uiHelpers.js';
|
import { showToast } from '../utils/uiHelpers.js';
|
||||||
|
import { escapeHtml, escapeAttribute } from './shared/utils.js';
|
||||||
|
|
||||||
export class SidebarManager {
|
export class SidebarManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -1294,15 +1295,19 @@ export class SidebarManager {
|
|||||||
const isExpanded = this.expandedNodes.has(currentPath);
|
const isExpanded = this.expandedNodes.has(currentPath);
|
||||||
const isSelected = this.selectedPath === currentPath;
|
const isSelected = this.selectedPath === currentPath;
|
||||||
|
|
||||||
|
const escapedPath = escapeAttribute(currentPath);
|
||||||
|
const escapedFolderName = escapeHtml(folderName);
|
||||||
|
const escapedTitle = escapeAttribute(folderName);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="sidebar-tree-node" data-path="${currentPath}">
|
<div class="sidebar-tree-node" data-path="${escapedPath}">
|
||||||
<div class="sidebar-tree-node-content ${isSelected ? 'selected' : ''}" data-path="${currentPath}">
|
<div class="sidebar-tree-node-content ${isSelected ? 'selected' : ''}" data-path="${escapedPath}">
|
||||||
<div class="sidebar-tree-expand-icon ${isExpanded ? 'expanded' : ''}"
|
<div class="sidebar-tree-expand-icon ${isExpanded ? 'expanded' : ''}"
|
||||||
style="${hasChildren ? '' : 'opacity: 0; pointer-events: none;'}">
|
style="${hasChildren ? '' : 'opacity: 0; pointer-events: none;'}">
|
||||||
<i class="fas fa-chevron-right"></i>
|
<i class="fas fa-chevron-right"></i>
|
||||||
</div>
|
</div>
|
||||||
<i class="fas fa-folder sidebar-tree-folder-icon"></i>
|
<i class="fas fa-folder sidebar-tree-folder-icon"></i>
|
||||||
<div class="sidebar-tree-folder-name" title="${folderName}">${folderName}</div>
|
<div class="sidebar-tree-folder-name" title="${escapedTitle}">${escapedFolderName}</div>
|
||||||
</div>
|
</div>
|
||||||
${hasChildren ? `
|
${hasChildren ? `
|
||||||
<div class="sidebar-tree-children ${isExpanded ? 'expanded' : ''}">
|
<div class="sidebar-tree-children ${isExpanded ? 'expanded' : ''}">
|
||||||
@@ -1342,12 +1347,15 @@ export class SidebarManager {
|
|||||||
const foldersHtml = this.foldersList.map(folder => {
|
const foldersHtml = this.foldersList.map(folder => {
|
||||||
const displayName = folder === '' ? '/' : folder;
|
const displayName = folder === '' ? '/' : folder;
|
||||||
const isSelected = this.selectedPath === folder;
|
const isSelected = this.selectedPath === folder;
|
||||||
|
const escapedPath = escapeAttribute(folder);
|
||||||
|
const escapedDisplayName = escapeHtml(displayName);
|
||||||
|
const escapedTitle = escapeAttribute(displayName);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="sidebar-folder-item ${isSelected ? 'selected' : ''}" data-path="${folder}">
|
<div class="sidebar-folder-item ${isSelected ? 'selected' : ''}" data-path="${escapedPath}">
|
||||||
<div class="sidebar-node-content" data-path="${folder}">
|
<div class="sidebar-node-content" data-path="${escapedPath}">
|
||||||
<i class="fas fa-folder sidebar-folder-icon"></i>
|
<i class="fas fa-folder sidebar-folder-icon"></i>
|
||||||
<div class="sidebar-folder-name" title="${displayName}">${displayName}</div>
|
<div class="sidebar-folder-name" title="${escapedTitle}">${escapedDisplayName}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -1570,7 +1578,8 @@ export class SidebarManager {
|
|||||||
|
|
||||||
// Add selection to current path
|
// Add selection to current path
|
||||||
if (this.selectedPath !== null && this.selectedPath !== undefined) {
|
if (this.selectedPath !== null && this.selectedPath !== undefined) {
|
||||||
const selectedItem = folderTree.querySelector(`[data-path="${this.selectedPath}"]`);
|
const escapedPathSelector = CSS.escape(this.selectedPath);
|
||||||
|
const selectedItem = folderTree.querySelector(`[data-path="${escapedPathSelector}"]`);
|
||||||
if (selectedItem) {
|
if (selectedItem) {
|
||||||
selectedItem.classList.add('selected');
|
selectedItem.classList.add('selected');
|
||||||
}
|
}
|
||||||
@@ -1581,7 +1590,8 @@ export class SidebarManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (this.selectedPath !== null && this.selectedPath !== undefined) {
|
if (this.selectedPath !== null && this.selectedPath !== undefined) {
|
||||||
const selectedNode = folderTree.querySelector(`[data-path="${this.selectedPath}"] .sidebar-tree-node-content`);
|
const escapedPathSelector = CSS.escape(this.selectedPath);
|
||||||
|
const selectedNode = folderTree.querySelector(`[data-path="${escapedPathSelector}"] .sidebar-tree-node-content`);
|
||||||
if (selectedNode) {
|
if (selectedNode) {
|
||||||
selectedNode.classList.add('selected');
|
selectedNode.classList.add('selected');
|
||||||
this.expandPathParents(this.selectedPath);
|
this.expandPathParents(this.selectedPath);
|
||||||
@@ -1655,7 +1665,7 @@ export class SidebarManager {
|
|||||||
const breadcrumbs = [`
|
const breadcrumbs = [`
|
||||||
<div class="breadcrumb-dropdown">
|
<div class="breadcrumb-dropdown">
|
||||||
<span class="sidebar-breadcrumb-item ${isRootSelected ? 'active' : ''}" data-path="">
|
<span class="sidebar-breadcrumb-item ${isRootSelected ? 'active' : ''}" data-path="">
|
||||||
<i class="fas fa-home"></i> ${this.apiClient.apiConfig.config.displayName} root
|
<i class="fas fa-home"></i> ${escapeHtml(this.apiClient.apiConfig.config.displayName)} root
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`];
|
`];
|
||||||
@@ -1675,8 +1685,8 @@ export class SidebarManager {
|
|||||||
</span>
|
</span>
|
||||||
<div class="breadcrumb-dropdown-menu">
|
<div class="breadcrumb-dropdown-menu">
|
||||||
${nextLevelFolders.map(folder => `
|
${nextLevelFolders.map(folder => `
|
||||||
<div class="breadcrumb-dropdown-item" data-path="${folder}">
|
<div class="breadcrumb-dropdown-item" data-path="${escapeAttribute(folder)}">
|
||||||
${folder}
|
${escapeHtml(folder)}
|
||||||
</div>`).join('')
|
</div>`).join('')
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -1692,12 +1702,14 @@ export class SidebarManager {
|
|||||||
|
|
||||||
// Get siblings for this level
|
// Get siblings for this level
|
||||||
const siblings = this.getSiblingFolders(parts, index);
|
const siblings = this.getSiblingFolders(parts, index);
|
||||||
|
const escapedCurrentPath = escapeAttribute(currentPath);
|
||||||
|
const escapedPart = escapeHtml(part);
|
||||||
|
|
||||||
breadcrumbs.push(`<span class="sidebar-breadcrumb-separator">/</span>`);
|
breadcrumbs.push(`<span class="sidebar-breadcrumb-separator">/</span>`);
|
||||||
breadcrumbs.push(`
|
breadcrumbs.push(`
|
||||||
<div class="breadcrumb-dropdown">
|
<div class="breadcrumb-dropdown">
|
||||||
<span class="sidebar-breadcrumb-item ${isLast ? 'active' : ''}" data-path="${currentPath}">
|
<span class="sidebar-breadcrumb-item ${isLast ? 'active' : ''}" data-path="${escapedCurrentPath}">
|
||||||
${part}
|
${escapedPart}
|
||||||
${siblings.length > 1 ? `
|
${siblings.length > 1 ? `
|
||||||
<span class="breadcrumb-dropdown-indicator">
|
<span class="breadcrumb-dropdown-indicator">
|
||||||
<i class="fas fa-caret-down"></i>
|
<i class="fas fa-caret-down"></i>
|
||||||
@@ -1706,11 +1718,14 @@ export class SidebarManager {
|
|||||||
</span>
|
</span>
|
||||||
${siblings.length > 1 ? `
|
${siblings.length > 1 ? `
|
||||||
<div class="breadcrumb-dropdown-menu">
|
<div class="breadcrumb-dropdown-menu">
|
||||||
${siblings.map(folder => `
|
${siblings.map(folder => {
|
||||||
<div class="breadcrumb-dropdown-item ${folder === part ? 'active' : ''}"
|
const siblingPath = parts.slice(0, index).concat(folder).join('/');
|
||||||
data-path="${currentPath.replace(part, folder)}">
|
return `
|
||||||
${folder}
|
<div class="breadcrumb-dropdown-item ${folder === part ? 'active' : ''}"
|
||||||
</div>`).join('')
|
data-path="${escapeAttribute(siblingPath)}">
|
||||||
|
${escapeHtml(folder)}
|
||||||
|
</div>`;
|
||||||
|
}).join('')
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
@@ -1732,8 +1747,8 @@ export class SidebarManager {
|
|||||||
</span>
|
</span>
|
||||||
<div class="breadcrumb-dropdown-menu">
|
<div class="breadcrumb-dropdown-menu">
|
||||||
${childFolders.map(folder => `
|
${childFolders.map(folder => `
|
||||||
<div class="breadcrumb-dropdown-item" data-path="${currentPath}/${folder}">
|
<div class="breadcrumb-dropdown-item" data-path="${escapeAttribute(currentPath + '/' + folder)}">
|
||||||
${folder}
|
${escapeHtml(folder)}
|
||||||
</div>`).join('')
|
</div>`).join('')
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user