280 lines
22 KiB
JavaScript
280 lines
22 KiB
JavaScript
// --- static/tree-ui.js ---
|
|
|
|
// ==========================================
|
|
// 🌟 輔助函數:全部展開與全部收合
|
|
// ==========================================
|
|
export function expandAll(elementId) {
|
|
const details = document.getElementById(`details-${elementId}`);
|
|
if (details) {
|
|
details.open = true; // 展開自己
|
|
const subDetails = details.querySelectorAll('details');
|
|
subDetails.forEach(d => d.open = true); // 展開所有子項目
|
|
}
|
|
}
|
|
|
|
export function collapseAll(elementId) {
|
|
const details = document.getElementById(`details-${elementId}`);
|
|
if (details) {
|
|
const subDetails = details.querySelectorAll('details');
|
|
subDetails.forEach(d => d.open = false); // 收合所有子項目
|
|
details.open = false; // 收合自己
|
|
}
|
|
}
|
|
|
|
// ==========================================
|
|
// 🌟 核心函數:生成完整設備配置樹狀圖
|
|
// ==========================================
|
|
export function buildTree(node, path = '', isParentCommandGroup = false) {
|
|
let html = '';
|
|
if (!node || typeof node !== 'object') return html;
|
|
|
|
for (const [key, value] of Object.entries(node)) {
|
|
const currentPath = path ? `${path}::${key}` : key;
|
|
let cliCommand = currentPath.replace(/::/g, ' ').replace(/\s*\[\d+\]/g, '');
|
|
|
|
if (typeof value === 'object' && value !== null) {
|
|
const hasNestedObject = Object.values(value).some(v => typeof v === 'object' && v !== null);
|
|
const isCommandGroup = isParentCommandGroup || !hasNestedObject;
|
|
const elementId = `folder-${currentPath.replace(/[^a-zA-Z0-9]/g, '-')}`;
|
|
const folderSuffix = isCommandGroup ? '<span style="color: #95a5a6; font-size: 0.85em; font-weight: normal; margin-left: 5px;">(指令群組)</span>' : '';
|
|
|
|
const svgFolderClosed = `<svg width="18" height="18" viewBox="0 0 24 24" fill="#f1c40f"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>`;
|
|
const svgFolderOpen = `<svg width="18" height="18" viewBox="0 0 24 24" fill="#f1c40f"><path d="M19 8H8.99C8.04 8 7.19 8.59 6.81 9.46L2.81 18.55C2.62 18.98 2.94 19.5 3.41 19.5H16.99C17.94 19.5 18.79 18.91 19.17 18.04L23.17 8.95C23.36 8.52 23.04 8 22.57 8H19zM4 4c-1.1 0-2 .9-2 2v10.59l3.09-7.04C5.58 8.36 6.27 8 7.01 8H19v-2c0-1.1-.9-2-2-2h-7l-2-2H4c-1.1 0-2 .9-2 2v12z"/></svg>`;
|
|
const svgGroupClosed = `<svg width="18" height="18" viewBox="0 0 24 24" fill="#95a5a6"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>`;
|
|
const svgGroupOpen = `<svg width="18" height="18" viewBox="0 0 24 24" fill="#3498db"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>`;
|
|
|
|
const iconClosed = isCommandGroup ? svgGroupClosed : svgFolderClosed;
|
|
const iconOpen = isCommandGroup ? svgGroupOpen : svgFolderOpen;
|
|
|
|
const expandAllIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 13 12 18 17 13"></polyline><polyline points="7 6 12 11 17 6"></polyline></svg>`;
|
|
const collapseAllIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="17 11 12 6 7 11"></polyline><polyline points="17 18 12 13 7 18"></polyline></svg>`;
|
|
|
|
const expandCollapseBtns = `
|
|
<span style="margin-left: 8px; display: inline-flex; gap: 6px; align-items: center;">
|
|
<span onclick="event.stopPropagation(); event.preventDefault(); expandAll('${elementId}')"
|
|
style="cursor: pointer; color: #95a5a6; transition: color 0.2s; display: flex; align-items: center;"
|
|
onmouseover="this.style.color='#3498db'" onmouseout="this.style.color='#95a5a6'" title="展開全部子項目">
|
|
${expandAllIcon}
|
|
</span>
|
|
<span onclick="event.stopPropagation(); event.preventDefault(); collapseAll('${elementId}')"
|
|
style="cursor: pointer; color: #95a5a6; transition: color 0.2s; display: flex; align-items: center;"
|
|
onmouseover="this.style.color='#e67e22'" onmouseout="this.style.color='#95a5a6'" title="收合全部子項目">
|
|
${collapseAllIcon}
|
|
</span>
|
|
</span>
|
|
`;
|
|
|
|
html += `
|
|
<details id="details-${elementId}" class="tree-folder" style="margin-top: 4px; margin-left: 20px;"
|
|
ontoggle="this.querySelector('.icon-closed').style.display = this.open ? 'none' : 'inline-block'; this.querySelector('.icon-open').style.display = this.open ? 'inline-block' : 'none';">
|
|
<summary class="tree-folder-header"
|
|
title="${cliCommand}"
|
|
onmouseover="this.style.backgroundColor='#f1f2f6'"
|
|
onmouseout="this.style.backgroundColor='transparent'"
|
|
style="cursor: pointer; padding: 4px 8px; display: flex; align-items: center; outline: none; border-radius: 4px; transition: background-color 0.2s;">
|
|
|
|
<span style="margin-right: 6px; display: inline-flex; align-items: center; width: 18px; height: 18px;">
|
|
<span class="icon-closed" style="display: inline-block;">${iconClosed}</span>
|
|
<span class="icon-open" style="display: none;">${iconOpen}</span>
|
|
</span>
|
|
<b style="color: #2c3e50;">${key}</b>${folderSuffix}
|
|
${expandCollapseBtns}
|
|
|
|
<div style="margin-left: auto; display: flex; align-items: center;">
|
|
<span id="edit-btn-${elementId}" onclick="event.stopPropagation(); event.preventDefault(); startEditFolder('${currentPath}', '${elementId}')"
|
|
style="cursor: pointer; font-size: 14px; opacity: 0.3; transition: opacity 0.2s;"
|
|
onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.3" title="鎖定並編輯此群組">
|
|
✏️
|
|
</span>
|
|
<span id="actions-${elementId}" style="display:none; margin-left: 10px;">
|
|
<button onclick="event.stopPropagation(); event.preventDefault(); previewFolderCLI('${currentPath}', '${elementId}')" style="background-color: #27ae60; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px;">✅ 預覽指令</button>
|
|
<button onclick="event.stopPropagation(); event.preventDefault(); cancelEditFolder('${currentPath}', '${elementId}')" style="background-color: #e74c3c; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: 5px;">❌ 取消</button>
|
|
</span>
|
|
</div>
|
|
</summary>
|
|
<div id="content-${elementId}" class="tree-folder-content" style="margin-left: 12px; border-left: 1px dashed #bdc3c7; padding-left: 12px;">
|
|
${buildTree(value, currentPath, isCommandGroup)}
|
|
</div>
|
|
</details>
|
|
`;
|
|
} else {
|
|
const isVirtualIndex = /^\[\d+\]$/.test(key);
|
|
const isNoCommand = (key === 'no');
|
|
const safeValue = (value === null ? '' : value).toString().replace(/"/g, """);
|
|
const elementId = `leaf-${currentPath.replace(/[^a-zA-Z0-9]/g, '-')}`;
|
|
|
|
if (isNoCommand) {
|
|
let baseCmd = cliCommand.replace(/(^|\s)no$/, '').trim();
|
|
cliCommand = baseCmd ? `${baseCmd} no ${safeValue}` : `no ${safeValue}`;
|
|
}
|
|
|
|
const svgLeaf = `<svg width="16" height="16" viewBox="0 0 24 24" fill="#bdc3c7"><path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"/></svg>`;
|
|
const svgDisabled = `<svg width="16" height="16" viewBox="0 0 24 24" fill="#e74c3c"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"/></svg>`;
|
|
|
|
const iconHtml = (icon) => `<span style="margin-right: 6px; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px;">${icon}</span>`;
|
|
const valueStyle = `color: #16a085; margin-left: 5px; font-family: Courier New, monospace; display: inline-block; transform: translateY(1.5px);`;
|
|
|
|
let displayContent = '';
|
|
|
|
if (isVirtualIndex) {
|
|
displayContent = `
|
|
${iconHtml(svgLeaf)}
|
|
<span class="leaf-container" id="container-${elementId}" data-path="${currentPath}" data-original="${safeValue}" style="display: inline-flex; align-items: center; min-height: 24px;">
|
|
<span class="leaf-value" style="${valueStyle}">${safeValue}</span>
|
|
</span>
|
|
`;
|
|
} else if (isNoCommand) {
|
|
displayContent = `
|
|
<span id="display-wrapper-${elementId}" style="display: inline-flex; align-items: center;">
|
|
${iconHtml(svgDisabled)}
|
|
<b style="color: #e74c3c;">no:</b>
|
|
<span class="leaf-container" id="container-${elementId}" data-path="${currentPath}" data-original="${safeValue}" style="display: inline-flex; align-items: center; min-height: 24px;">
|
|
<span class="leaf-value" style="color: #7f8c8d; margin-left: 5px; font-family: Courier New, monospace; display: inline-block; transform: translateY(1.5px);">${safeValue}</span>
|
|
</span>
|
|
</span>
|
|
`;
|
|
} else {
|
|
displayContent = `
|
|
${iconHtml(svgLeaf)} <b>${key}</b>:
|
|
<span class="leaf-container" id="container-${elementId}" data-path="${currentPath}" data-original="${safeValue}" style="display: inline-flex; align-items: center; min-height: 24px;">
|
|
<span class="leaf-value" style="${valueStyle}">${safeValue}</span>
|
|
</span>
|
|
`;
|
|
}
|
|
|
|
html += `
|
|
<div class="tree-leaf-node"
|
|
title="${cliCommand}"
|
|
onmouseover="this.style.backgroundColor='#f1f2f6'"
|
|
onmouseout="this.style.backgroundColor='transparent'"
|
|
style="padding: 4px 8px; color: #34495e; margin-left: 20px; border-left: 1px solid #ecf0f1; display: flex; align-items: center; border-radius: 4px; transition: background-color 0.2s;">
|
|
${displayContent}
|
|
|
|
<div style="margin-left: auto; display: flex; align-items: center;">
|
|
<span id="edit-btn-${elementId}" onclick="event.stopPropagation(); event.preventDefault(); startEditLeaf('${currentPath}', '${elementId}')"
|
|
style="cursor: pointer; font-size: 14px; opacity: 0.3; transition: opacity 0.2s;"
|
|
onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.3" title="鎖定並編輯此項目">
|
|
✏️
|
|
</span>
|
|
<span id="actions-${elementId}" style="display:none; margin-left: 10px;">
|
|
<button onclick="event.stopPropagation(); event.preventDefault(); previewLeafCLI('${currentPath}', '${elementId}')" style="background-color: #27ae60; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px;">✅ 預覽指令</button>
|
|
<button onclick="event.stopPropagation(); event.preventDefault(); cancelEditLeaf('${currentPath}', '${elementId}')" style="background-color: #e74c3c; color: white; border: none; padding: 2px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: 5px;">❌ 取消</button>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
return html;
|
|
}
|
|
|
|
// ==========================================
|
|
// 🌟 核心函數:生成系統設定的過濾樹狀圖
|
|
// ==========================================
|
|
export function buildRealFilterTree(data, parentPath = '', hiddenKeys = [], isParentCommandGroup = false) {
|
|
if (typeof data !== 'object' || data === null) return '';
|
|
|
|
let html = `<div style="margin-left: ${parentPath ? '24px' : '0'};">`;
|
|
|
|
for (const key in data) {
|
|
const value = data[key];
|
|
const currentPath = parentPath ? `${parentPath}::${key}` : key;
|
|
const isChecked = hiddenKeys.includes(currentPath) ? "checked" : "";
|
|
|
|
let cliCommand = currentPath.replace(/::/g, ' ').replace(/\s*\[\d+\]/g, '');
|
|
|
|
const svgFolderClosed = `<svg width="18" height="18" viewBox="0 0 24 24" fill="#f1c40f"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>`;
|
|
const svgFolderOpen = `<svg width="18" height="18" viewBox="0 0 24 24" fill="#f1c40f"><path d="M19 8H8.99C8.04 8 7.19 8.59 6.81 9.46L2.81 18.55C2.62 18.98 2.94 19.5 3.41 19.5H16.99C17.94 19.5 18.79 18.91 19.17 18.04L23.17 8.95C23.36 8.52 23.04 8 22.57 8H19zM4 4c-1.1 0-2 .9-2 2v10.59l3.09-7.04C5.58 8.36 6.27 8 7.01 8H19v-2c0-1.1-.9-2-2-2h-7l-2-2H4c-1.1 0-2 .9-2 2v12z"/></svg>`;
|
|
const svgGroupClosed = `<svg width="18" height="18" viewBox="0 0 24 24" fill="#95a5a6"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>`;
|
|
const svgGroupOpen = `<svg width="18" height="18" viewBox="0 0 24 24" fill="#3498db"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>`;
|
|
const svgLeaf = `<svg width="16" height="16" viewBox="0 0 24 24" fill="#bdc3c7"><path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"/></svg>`;
|
|
|
|
const isFolder = typeof value === 'object' && value !== null && Object.keys(value).length > 0;
|
|
const onChangeEvent = isFolder ? 'toggleChildCheckboxes(this)' : 'updateParentCheckboxState(this)';
|
|
const checkboxHtml = `<input type="checkbox" value="${currentPath}" class="filter-checkbox" ${isChecked} onclick="event.stopPropagation();" onchange="${onChangeEvent}" style="margin-right: 8px; cursor: pointer; width: 14px; height: 14px; flex-shrink: 0;">`;
|
|
|
|
if (isFolder) {
|
|
const hasNestedObject = Object.values(value).some(v => typeof v === 'object' && v !== null);
|
|
const isCommandGroup = isParentCommandGroup || !hasNestedObject;
|
|
const elementId = `filter-folder-${currentPath.replace(/[^a-zA-Z0-9]/g, '-')}`;
|
|
|
|
const iconClosed = isCommandGroup ? svgGroupClosed : svgFolderClosed;
|
|
const iconOpen = isCommandGroup ? svgGroupOpen : svgFolderOpen;
|
|
const folderSuffix = isCommandGroup ? `<span style="color: #95a5a6; font-size: 0.85em; font-weight: normal; margin-left: 5px;">(指令群組)</span>` : '';
|
|
|
|
const expandAllIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 13 12 18 17 13"></polyline><polyline points="7 6 12 11 17 6"></polyline></svg>`;
|
|
const collapseAllIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="17 11 12 6 7 11"></polyline><polyline points="17 18 12 13 7 18"></polyline></svg>`;
|
|
|
|
const expandCollapseBtns = `
|
|
<span style="margin-left: 8px; display: inline-flex; gap: 6px; align-items: center;">
|
|
<span onclick="event.stopPropagation(); event.preventDefault(); expandAll('${elementId}')"
|
|
style="cursor: pointer; color: #95a5a6; transition: color 0.2s; display: flex; align-items: center;"
|
|
onmouseover="this.style.color='#3498db'" onmouseout="this.style.color='#95a5a6'" title="展開全部子項目">
|
|
${expandAllIcon}
|
|
</span>
|
|
<span onclick="event.stopPropagation(); event.preventDefault(); collapseAll('${elementId}')"
|
|
style="cursor: pointer; color: #95a5a6; transition: color 0.2s; display: flex; align-items: center;"
|
|
onmouseover="this.style.color='#e67e22'" onmouseout="this.style.color='#95a5a6'" title="收合全部子項目">
|
|
${collapseAllIcon}
|
|
</span>
|
|
</span>
|
|
`;
|
|
|
|
html += `
|
|
<details id="details-${elementId}" class="tree-folder" ontoggle="this.querySelector('.icon-closed').style.display = this.open ? 'none' : 'inline-block'; this.querySelector('.icon-open').style.display = this.open ? 'inline-block' : 'none';">
|
|
<summary class="tree-node-header" title="${cliCommand}" style="font-weight: bold; color: #2c3e50; margin-top: 4px; list-style: none; display: flex; align-items: center; cursor: pointer; outline: none; padding: 4px 0;">
|
|
<style>#details-${elementId} > summary::-webkit-details-marker { display: none; }</style>
|
|
${checkboxHtml}
|
|
<span style="margin-right: 6px; display: inline-flex; align-items: center; width: 18px; height: 18px;">
|
|
<span class="icon-closed" style="display: inline-block;">${iconClosed}</span>
|
|
<span class="icon-open" style="display: none;">${iconOpen}</span>
|
|
</span>
|
|
<span>${key}${folderSuffix}</span>
|
|
${expandCollapseBtns}
|
|
</summary>
|
|
<div class="filter-children-container" style="border-left: 1px dashed #bdc3c7; margin-left: 7px; padding-left: 16px;">
|
|
${buildRealFilterTree(value, currentPath, hiddenKeys, isCommandGroup)}
|
|
</div>
|
|
</details>
|
|
`;
|
|
} else {
|
|
const isVirtualIndex = /^\[\d+\]$/.test(key);
|
|
let displayName = isVirtualIndex ? `<span style="color: #95a5a6; font-weight: normal;">項目 ${key}</span>` : `<b>${key}</b>`;
|
|
const safeValue = (value === null ? '' : value).toString().replace(/"/g, """);
|
|
|
|
const isNoCommand = (key === 'no');
|
|
|
|
let valueHtml = `<span style="color: #16a085; font-family: Courier New, monospace; display: inline-block; transform: translateY(1.5px);">${safeValue}</span>`;
|
|
let colonHtml = `:`;
|
|
let currentIcon = `<svg width="16" height="16" viewBox="0 0 24 24" fill="#bdc3c7"><path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"/></svg>`;
|
|
|
|
if (isNoCommand) {
|
|
let baseCmd = cliCommand.replace(/(^|\s)no$/, '').trim();
|
|
cliCommand = baseCmd ? `${baseCmd} no ${safeValue}` : `no ${safeValue}`;
|
|
|
|
displayName = `<b style="color: #e74c3c;">no</b>`;
|
|
colonHtml = `:`;
|
|
valueHtml = `<span style="color: #7f8c8d; margin-left: 5px; font-family: Courier New, monospace; display: inline-block; transform: translateY(1.5px);">${safeValue}</span>`;
|
|
currentIcon = `<svg width="16" height="16" viewBox="0 0 24 24" fill="#e74c3c"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"/></svg>`;
|
|
}
|
|
|
|
html += `
|
|
<div class="tree-leaf-node"
|
|
onmouseover="this.style.backgroundColor='#f1f2f6'"
|
|
onmouseout="this.style.backgroundColor='transparent'"
|
|
style="padding: 2px 0; color: #34495e; display: flex; align-items: center; margin-top: 2px; border-radius: 4px; transition: background-color 0.2s;"
|
|
title="${cliCommand}">
|
|
${checkboxHtml}
|
|
<span style="margin-right: 6px; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; flex-shrink: 0;">
|
|
${currentIcon}
|
|
</span>
|
|
<span style="margin-right: 5px;">${displayName}${colonHtml}</span>
|
|
${valueHtml}
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
html += '</div>';
|
|
return html;
|
|
}
|