[DRAFT] feat(menu): use material icons for files and folders (#2739)

feat(menu): use material icons for files and folders
This commit is contained in:
Dicha Zelianivan Arkana 2025-04-23 02:39:45 +07:00 committed by GitHub
parent 0d561f8a27
commit 1a376c262e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 19 deletions

7
package-lock.json generated
View file

@ -61,6 +61,7 @@
"tmp": "^0.2.3",
"tree-sitter-wasms": "^0.1.11",
"turndown": "^7.2.0",
"vscode-material-icons": "^0.1.1",
"web-tree-sitter": "^0.22.6",
"zod": "^3.23.8"
},
@ -21550,6 +21551,12 @@
"node": ">= 0.8"
}
},
"node_modules/vscode-material-icons": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/vscode-material-icons/-/vscode-material-icons-0.1.1.tgz",
"integrity": "sha512-GsoEEF8Tbb0yUFQ6N6FPvh11kFkL9F95x0FkKlbbfRQN9eFms67h+L3t6b9cUv58dSn2gu8kEhNfoESVCrz4ag==",
"license": "MIT"
},
"node_modules/walker": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",

View file

@ -453,6 +453,7 @@
"tmp": "^0.2.3",
"tree-sitter-wasms": "^0.1.11",
"turndown": "^7.2.0",
"vscode-material-icons": "^0.1.1",
"web-tree-sitter": "^0.22.6",
"zod": "^3.23.8"
},

View file

@ -622,6 +622,13 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
"codicon.css",
])
const materialIconsUri = getUri(webview, this.contextProxy.extensionUri, [
"node_modules",
"vscode-material-icons",
"generated",
"icons",
])
const imagesUri = getUri(webview, this.contextProxy.extensionUri, ["assets", "images"])
const file = "src/index.tsx"
@ -656,7 +663,8 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
<link rel="stylesheet" type="text/css" href="${stylesUri}">
<link href="${codiconsUri}" rel="stylesheet" />
<script nonce="${nonce}">
window.IMAGES_BASE_URI = "${imagesUri}"
window.IMAGES_BASE_URI = "${imagesUri}"
window.MATERIAL_ICONS_BASE_URI = "${materialIconsUri}"
</script>
<title>Roo Code</title>
</head>
@ -706,6 +714,14 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
"codicon.css",
])
// The material icons from the React build output
const materialIconsUri = getUri(webview, this.contextProxy.extensionUri, [
"node_modules",
"vscode-material-icons",
"generated",
"icons",
])
const imagesUri = getUri(webview, this.contextProxy.extensionUri, ["assets", "images"])
// const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "main.js"))
@ -742,6 +758,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
<link href="${codiconsUri}" rel="stylesheet" />
<script nonce="${nonce}">
window.IMAGES_BASE_URI = "${imagesUri}"
window.MATERIAL_ICONS_BASE_URI = "${materialIconsUri}"
</script>
<title>Roo Code</title>
</head>

View file

@ -1,4 +1,5 @@
import React, { useEffect, useMemo, useRef } from "react"
import React, { useEffect, useMemo, useRef, useState } from "react"
import { getIconForFilePath, getIconUrlByName, getIconForDirectoryPath } from "vscode-material-icons"
import {
ContextMenuOptionType,
ContextMenuQueryItem,
@ -35,6 +36,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
loading = false,
dynamicSearchResults = [],
}) => {
const [materialIconsBaseUri, setMaterialIconsBaseUri] = useState("")
const menuRef = useRef<HTMLDivElement>(null)
const filteredOptions = useMemo(() => {
@ -57,6 +59,12 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
}
}, [selectedIndex])
// get the icons base uri on mount
useEffect(() => {
const w = window as any
setMaterialIconsBaseUri(w.MATERIAL_ICONS_BASE_URI)
}, [])
const renderOptionContent = (option: ContextMenuQueryItem) => {
switch (option.type) {
case ContextMenuOptionType.Mode:
@ -175,6 +183,15 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
}
}
const getMaterialIconForOption = (option: ContextMenuQueryItem): string => {
// only take the last part of the path to handle both file and folder icons
// since material-icons have specific folder icons, we use them if available
const name = option.value?.split("/").filter(Boolean).at(-1) ?? ""
const iconName =
option.type === ContextMenuOptionType.Folder ? getIconForDirectoryPath(name) : getIconForFilePath(name)
return getIconUrlByName(iconName, materialIconsBaseUri)
}
const isOptionSelectable = (option: ContextMenuQueryItem): boolean => {
return option.type !== ContextMenuOptionType.NoResults && option.type !== ContextMenuOptionType.URL
}
@ -231,17 +248,35 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
overflow: "hidden",
paddingTop: 0,
}}>
{option.type !== ContextMenuOptionType.Mode && getIconForOption(option) && (
<i
className={`codicon codicon-${getIconForOption(option)}`}
{(option.type === ContextMenuOptionType.File ||
option.type === ContextMenuOptionType.Folder ||
option.type === ContextMenuOptionType.OpenedFile) && (
<img
src={getMaterialIconForOption(option)}
alt="Mode"
style={{
marginRight: "6px",
flexShrink: 0,
fontSize: "14px",
marginTop: 0,
width: "16px",
height: "16px",
}}
/>
)}
{option.type !== ContextMenuOptionType.Mode &&
option.type !== ContextMenuOptionType.File &&
option.type !== ContextMenuOptionType.Folder &&
option.type !== ContextMenuOptionType.OpenedFile &&
getIconForOption(option) && (
<i
className={`codicon codicon-${getIconForOption(option)}`}
style={{
marginRight: "6px",
flexShrink: 0,
fontSize: "14px",
marginTop: 0,
}}
/>
)}
{renderOptionContent(option)}
</div>
{(option.type === ContextMenuOptionType.File ||
@ -253,18 +288,6 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
style={{ fontSize: "10px", flexShrink: 0, marginLeft: 8 }}
/>
)}
{(option.type === ContextMenuOptionType.Problems ||
option.type === ContextMenuOptionType.Terminal ||
((option.type === ContextMenuOptionType.File ||
option.type === ContextMenuOptionType.Folder ||
option.type === ContextMenuOptionType.OpenedFile ||
option.type === ContextMenuOptionType.Git) &&
option.value)) && (
<i
className="codicon codicon-add"
style={{ fontSize: "10px", flexShrink: 0, marginLeft: 8 }}
/>
)}
</div>
))
) : (