Integrate Lucide icons and replace inline SVGs

Add Lucide icon library and switch many inline SVGs to programmatically generated Lucide icons for consistent styling and reuse. Adds frontend/vendor/lucide.min.js and includes it in index.html; adds helper functions (escapeSvgAttr, svgAttrs, lucideIcon) in app.js and replaces numerous hard-coded SVG strings with Lucide-generated icons and CSS utility classes. Update components.css with .gd-lucide-icon, color helpers for folder/file icons, and remove a global stroke-width override so icon strokeWidth can be controlled per icon. Small accessibility and UI tweaks: use aria-label/title on several buttons and swap icons in modals, menus and toolbars. Also updates package.json/package-lock.json (dependency adjustments).
This commit is contained in:
2026-05-13 13:37:31 +12:00
parent 558cd3ddd7
commit 7753abd896
6 changed files with 118 additions and 30 deletions
+79 -29
View File
@@ -56,19 +56,69 @@ const defaultBranchName = "main";
const DEFAULT_EDITOR_VALUE = "__default_code__";
const CUSTOM_EDITOR_VALUE = "__custom__";
const FOLDER_ICON = `<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path fill="#54aeff" d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"/></svg>`;
const FILE_ICON = `<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path fill="#848d97" d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"/></svg>`;
const LOCAL_REPO_ICON = `<svg width="18" height="18" viewBox="0 0 16 16" aria-hidden="true" fill="none"><rect x="1.5" y="3" width="13" height="8.5" rx="1.25" stroke="currentColor" stroke-width="1.3"/><path d="M6 13.75h4M8 11.5v2.25" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>`;
const BRANCH_ICON = `<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg>`;
const SYNC_ICON = `<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 2.5a5.487 5.487 0 0 0-4.131 1.869l1.204 1.204A.25.25 0 0 1 4.896 6H1.25A.25.25 0 0 1 1 5.75V2.104a.25.25 0 0 1 .427-.177l1.38 1.38A7 7 0 0 1 14.95 7.16a.75.75 0 1 1-1.49.178A5.501 5.501 0 0 0 8 2.5ZM1.705 8.005a.75.75 0 0 1 .834.656 5.501 5.501 0 0 0 9.592 2.97l-1.204-1.204a.25.25 0 0 1 .177-.427h3.646a.25.25 0 0 1 .25.25v3.646a.25.25 0 0 1-.427.177l-1.38-1.38A7 7 0 0 1 1.05 8.84a.75.75 0 0 1 .656-.834Z"/></svg>`;
const PULL_ICON = `<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M7.25 1.75a.75.75 0 0 1 1.5 0v8.69l2.72-2.72a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734l-4 4a.75.75 0 0 1-1.06 0l-4-4A.749.749 0 0 1 4.53 7.72l2.72 2.72V1.75ZM2.75 14a.75.75 0 0 1 .75-.75h9a.75.75 0 0 1 0 1.5h-9a.75.75 0 0 1-.75-.75Z"/></svg>`;
const PUSH_ICON = `<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M7.47 3.22a.75.75 0 0 1 1.06 0l4 4A.749.749 0 0 1 12 8.5a.749.749 0 0 1-.53-.22L8.75 5.56v8.69a.75.75 0 0 1-1.5 0V5.56L4.53 8.28A.749.749 0 0 1 3.255 7.954a.749.749 0 0 1 .215-.734l4-4ZM2.75 2a.75.75 0 0 1 .75-.75h9a.75.75 0 0 1 0 1.5h-9A.75.75 0 0 1 2.75 2Z"/></svg>`;
const PUBLISH_ICON = `<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 1.25a.75.75 0 0 1 .75.75v1.13A3.001 3.001 0 0 1 11 6v.5h1.25A1.75 1.75 0 0 1 14 8.25v4A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-4A1.75 1.75 0 0 1 3.75 6.5H5V6a3.001 3.001 0 0 1 2.25-2.87V2A.75.75 0 0 1 8 1.25ZM6.5 6v.5h3V6a1.5 1.5 0 0 0-3 0Zm-.53 4.28 1.5-1.5a.75.75 0 0 1 1.06 0l1.5 1.5a.75.75 0 1 1-1.06 1.06l-.22-.22v1.13a.75.75 0 0 1-1.5 0v-1.13l-.22.22a.75.75 0 0 1-1.06-1.06Z"/></svg>`;
const EXPLORER_ICON = `<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M1.75 2A1.75 1.75 0 0 0 0 3.75v8.5C0 13.216.784 14 1.75 14h12.5A1.75 1.75 0 0 0 16 12.25v-6.5A1.75 1.75 0 0 0 14.25 4H7.5a.25.25 0 0 1-.2-.1l-.9-1.2A1.75 1.75 0 0 0 5 2H1.75Zm0 1.5H5a.25.25 0 0 1 .2.1l.9 1.2c.331.441.85.7 1.4.7h6.75a.25.25 0 0 1 .25.25v6.5a.25.25 0 0 1-.25.25H1.75a.25.25 0 0 1-.25-.25v-8.5a.25.25 0 0 1 .25-.25Z"/></svg>`;
const EDITOR_ICON = `<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-7.7 7.7a1.75 1.75 0 0 1-.744.44l-3.018.862a.75.75 0 0 1-.927-.927l.862-3.018a1.75 1.75 0 0 1 .44-.744l7.527-7.873ZM12.427 2.487a.25.25 0 0 0-.354 0l-.963.963 1.44 1.44.963-.963a.25.25 0 0 0 0-.354l-1.086-1.086ZM11.49 5.95l-1.44-1.44-5.503 5.503a.25.25 0 0 0-.063.106l-.558 1.955 1.955-.558a.25.25 0 0 0 .106-.063L11.49 5.95ZM1 14.25a.75.75 0 0 1 .75-.75h12.5a.75.75 0 0 1 0 1.5H1.75a.75.75 0 0 1-.75-.75Z"/></svg>`;
const WINDOW_MINIMIZE_ICON = `<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path fill="currentColor" d="M2 6.75h8v1.5H2z"/></svg>`;
const WINDOW_MAXIMIZE_ICON = `<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M2.75 2h6.5c.414 0 .75.336.75.75v6.5a.75.75 0 0 1-.75.75h-6.5A.75.75 0 0 1 2 9.25v-6.5c0-.414.336-.75.75-.75Zm.75 1.5v5h5v-5h-5Z"/></svg>`;
const WINDOW_CLOSE_ICON = `<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path fill="currentColor" d="M3.28 2.22a.75.75 0 0 0-1.06 1.06L4.94 6 2.22 8.72a.75.75 0 1 0 1.06 1.06L6 7.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L7.06 6l2.72-2.72a.75.75 0 0 0-1.06-1.06L6 4.94 3.28 2.22Z"/></svg>`;
function escapeSvgAttr(value = "") {
return String(value)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;");
}
function svgAttrs(attrs = {}) {
return Object.entries(attrs)
.filter(([, value]) => value !== undefined && value !== null && value !== false)
.map(([key, value]) => `${key}="${escapeSvgAttr(value)}"`)
.join(" ");
}
function lucideIcon(name, { size = 16, className = "", strokeWidth = 2, attrs = {} } = {}) {
const icon = window.lucide?.icons?.[name];
if (!icon) return "";
const iconAttrs = svgAttrs({
xmlns: "http://www.w3.org/2000/svg",
width: size,
height: size,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": strokeWidth,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"aria-hidden": "true",
focusable: "false",
class: ["gd-lucide-icon", className].filter(Boolean).join(" "),
...attrs,
});
const children = icon.map(([tag, childAttrs]) => `<${tag} ${svgAttrs(childAttrs)} />`).join("");
return `<svg ${iconAttrs}>${children}</svg>`;
}
const FOLDER_ICON = lucideIcon("Folder", { size: 16, className: "gd-folder-icon" });
const FILE_ICON = lucideIcon("File", { size: 16, className: "gd-file-icon" });
const LOCAL_REPO_ICON = lucideIcon("Monitor", { size: 18 });
const BRANCH_ICON = lucideIcon("GitBranch", { size: 16 });
const SYNC_ICON = lucideIcon("RefreshCw", { size: 15 });
const PULL_ICON = lucideIcon("Download", { size: 15 });
const PUSH_ICON = lucideIcon("Upload", { size: 15 });
const PUBLISH_ICON = lucideIcon("CloudUpload", { size: 15 });
const EXPLORER_ICON = lucideIcon("FolderOpen", { size: 15 });
const EDITOR_ICON = lucideIcon("SquarePen", { size: 15 });
const WINDOW_MINIMIZE_ICON = lucideIcon("Minus", { size: 12 });
const WINDOW_MAXIMIZE_ICON = lucideIcon("Square", { size: 12 });
const WINDOW_CLOSE_ICON = lucideIcon("X", { size: 12 });
const CHEVRON_DOWN_ICON = lucideIcon("ChevronDown", { size: 10, className: "gd-cell-caret" });
const MORE_ICON = lucideIcon("Ellipsis", { size: 15 });
const REFRESH_ICON = lucideIcon("RefreshCw", { size: 14 });
const REPOSITORY_ICON = lucideIcon("BookOpen", { size: 14 });
const CLONE_ICON = lucideIcon("GitFork", { size: 14 });
const SERVER_ICON = lucideIcon("Server", { size: 14 });
const SETTINGS_ICON = lucideIcon("Settings", { size: 14 });
const PLUS_ICON = lucideIcon("Plus", { size: 14 });
const CHECK_ICON = lucideIcon("Check", { size: 13 });
const EMPTY_DIFF_ICON = lucideIcon("FileDiff", { size: 44, strokeWidth: 1.6 });
const EMPTY_FILE_ICON = lucideIcon("File", { size: 44, strokeWidth: 1.6 });
const EMPTY_REPO_ICON = lucideIcon("FolderGit2", { size: 44, strokeWidth: 1.6 });
function currentPlatform() {
const platform = navigator.userAgentData?.platform || navigator.platform || "";
@@ -448,12 +498,12 @@ function renderDiff(diff = "") {
function emptyStateIcon(type = "repo") {
if (type === "diff") {
return `<svg viewBox="0 0 96 96" aria-hidden="true"><rect x="19" y="14" width="58" height="68" rx="8"/><path d="M34 34h28M34 48h18M34 62h28"/></svg>`;
return EMPTY_DIFF_ICON;
}
if (type === "file") {
return `<svg viewBox="0 0 96 96" aria-hidden="true"><path d="M27 12h28l18 18v54H27z"/><path d="M55 12v20h18"/></svg>`;
return EMPTY_FILE_ICON;
}
return `<svg viewBox="0 0 96 96" aria-hidden="true"><path d="M18 30h60v42a8 8 0 0 1-8 8H26a8 8 0 0 1-8-8z"/><path d="M26 22h20l8 8"/></svg>`;
return EMPTY_REPO_ICON;
}
function workflowEmptyStateTemplate({ title, message, icon = "repo", actions = true }) {
@@ -908,7 +958,7 @@ function reposModalContent(state) {
</div>
<div class="row" style="gap:5px;flex-shrink:0">
<button class="open-recent-repo-btn" data-recent-repo-path="${escapeHtml(path)}" type="button">Open</button>
<button class="remove-recent-repo-btn danger" data-recent-repo-path="${escapeHtml(path)}" type="button" title="Remove from list">×</button>
<button class="remove-recent-repo-btn danger" data-recent-repo-path="${escapeHtml(path)}" type="button" title="Remove from list" aria-label="Remove from list">${WINDOW_CLOSE_ICON}</button>
</div>
</div>`).join("")
: `<div class="gd-modal-empty">No recent repositories</div>`}
@@ -1007,7 +1057,7 @@ function serversModalContent(state) {
<div class="gd-modal-section">
<div class="gd-modal-section-header">
<h4 class="gd-modal-section-title" style="margin:0">Connected Servers</h4>
<button id="add-server-btn" type="button" class="primary">+ Add Server</button>
<button id="add-server-btn" type="button" class="primary">${PLUS_ICON} Add Server</button>
</div>
${settingsNotice ? `<div class="muted" style="font-size:12px;margin-bottom:8px">${escapeHtml(settingsNotice)}</div>` : ""}
${servers.length
@@ -1016,7 +1066,7 @@ function serversModalContent(state) {
<div class="gd-server-name">${escapeHtml(server.displayName)}</div>
<div class="gd-server-url muted">${escapeHtml(server.serverUrl)}</div>
<div class="row" style="gap:4px;margin-top:6px">
<button class="set-default-server-btn" data-id="${server.id}" type="button" style="font-size:12px;padding:2px 8px">${server.id === state.settings.activeServerId ? "✓ Active" : "Set active"}</button>
<button class="set-default-server-btn" data-id="${server.id}" type="button" style="font-size:12px;padding:2px 8px">${server.id === state.settings.activeServerId ? `${CHECK_ICON} Active` : "Set active"}</button>
<button class="edit-server-btn" data-id="${server.id}" type="button" style="font-size:12px;padding:2px 8px">Edit</button>
<button class="delete-server-btn danger" data-id="${server.id}" type="button" style="font-size:12px;padding:2px 8px">Remove</button>
</div>
@@ -1130,7 +1180,7 @@ function viewerModalContent(state) {
<select id="viewer-branch-select" style="width:auto;font-size:12px;padding:3px 8px" ${viewer.loading ? "disabled" : ""}>
${viewer.branches.map((b) => `<option value="${escapeHtml(b.name)}" ${b.name === viewer.branch ? "selected" : ""}>${escapeHtml(b.name)}${b.current ? " ✓" : ""}</option>`).join("")}
</select>
<button id="viewer-refresh-btn" type="button" style="padding:3px 8px;font-size:12px">↻</button>
<button id="viewer-refresh-btn" type="button" style="padding:3px 8px;font-size:12px" aria-label="Refresh viewer" title="Refresh viewer">${REFRESH_ICON}</button>
</div>
</div>
<div class="gd-viewer-tree">
@@ -1173,7 +1223,7 @@ function modalTemplate(state) {
<div class="gd-modal-header">
<h3 class="gd-modal-title">${cfg.title}</h3>
<button id="modal-close-btn" type="button" class="gd-modal-close" aria-label="Close">
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"/></svg>
${WINDOW_CLOSE_ICON}
</button>
</div>
<div class="gd-modal-body">
@@ -1233,7 +1283,7 @@ function dashboardView() {
<span class="gd-cell-label">Current branch</span>
<span class="gd-cell-value">${escapeHtml(displayBranchName)}</span>
</span>
<svg class="gd-cell-caret" viewBox="0 0 16 16" width="10" height="10" aria-hidden="true"><path fill="currentColor" d="M4.427 7.427l3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"/></svg>
${CHEVRON_DOWN_ICON}
</button>
${state.branches.menuOpen ? `<div class="branch-menu">
<div class="branch-menu-section">
@@ -1275,20 +1325,20 @@ function dashboardView() {
</div>
<div class="gd-utility-wrap">
<button id="utility-menu-btn" class="gd-utility-btn" type="button" title="More options" aria-label="More options">
<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"/></svg>
${MORE_ICON}
</button>
${utilityMenuOpen ? `<div class="gd-utility-menu" role="menu">
<button class="gd-utility-menu-item" data-open-modal="repos" type="button" role="menuitem">
<svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8Z"/></svg>
${REPOSITORY_ICON}
Open Repository
</button>
<button class="gd-utility-menu-item" data-open-modal="clone" type="button" role="menuitem">
<svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/></svg>
${CLONE_ICON}
Clone Repository
</button>
<div class="gd-utility-separator"></div>
<button class="gd-utility-menu-item" data-open-modal="viewer" type="button" role="menuitem">
<svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"/></svg>
${FILE_ICON}
File Viewer
</button>
<button class="gd-utility-menu-item" id="open-file-explorer-menu-btn" type="button" role="menuitem" ${!hasLocalRepo ? "disabled" : ""}>
@@ -1301,11 +1351,11 @@ function dashboardView() {
</button>
<div class="gd-utility-separator"></div>
<button class="gd-utility-menu-item" data-open-modal="servers" type="button" role="menuitem">
<svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M1.5 1.75a.25.25 0 0 1 .25-.25h12.5a.25.25 0 0 1 .25.25v4a.25.25 0 0 1-.25.25H1.75a.25.25 0 0 1-.25-.25v-4Zm.25-1.75A1.75 1.75 0 0 0 0 1.75v4C0 6.716.784 7.5 1.75 7.5h12.5A1.75 1.75 0 0 0 16 5.75v-4A1.75 1.75 0 0 0 14.25 0H1.75ZM0 10.25C0 9.284.784 8.5 1.75 8.5h12.5A1.75 1.75 0 0 1 16 10.25v4A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25v-4Zm1.5.25a.25.25 0 0 0-.25.25v4c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-4a.25.25 0 0 0-.25-.25H1.75Z"/></svg>
${SERVER_ICON}
Servers
</button>
<button class="gd-utility-menu-item" data-open-modal="settings" type="button" role="menuitem">
<svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.068.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.039.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.156-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.068-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.156.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.531.01 7.765 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM8 5.25a2.75 2.75 0 1 0 0 5.5 2.75 2.75 0 0 0 0-5.5ZM4.75 8a3.25 3.25 0 1 1 6.5 0 3.25 3.25 0 0 1-6.5 0Z"/></svg>
${SETTINGS_ICON}
Settings
</button>
</div>` : ""}
@@ -1354,7 +1404,7 @@ function dashboardView() {
leftContentHTML = `
<div class="gd-changes-search-row">
<button class="gd-filter-menu-btn" id="refresh-changes-btn" type="button" aria-label="Refresh" title="Refresh changes">
<svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 2.5a5.5 5.5 0 1 0 5.5 5.5.75.75 0 0 1 1.5 0 7 7 0 1 1-2.05-4.95V1.75a.75.75 0 0 1 1.5 0v3.5a.75.75 0 0 1-.75.75h-3.5a.75.75 0 0 1 0-1.5h2.19A5.48 5.48 0 0 0 8 2.5Z"/></svg>
${REFRESH_ICON}
</button>
<input id="changes-filter-input" class="gd-filter-input" placeholder="Filter changed files" value="${escapeHtml(state.changesFilter)}" />
</div>