Merge pull request #471 from voideditor/model-selection

Misc fixes
This commit is contained in:
Andrew Pareles 2025-05-07 19:56:13 -07:00 committed by GitHub
commit 77d174edcf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 189 additions and 179 deletions

View file

@ -71,7 +71,7 @@
"type": "string", "type": "string",
"description": "The URL from where the vscode server will be downloaded. You can use the following variables and they will be replaced dynamically:\n- ${quality}: vscode server quality, e.g. stable or insiders\n- ${version}: vscode server version, e.g. 1.69.0\n- ${commit}: vscode server release commit\n- ${arch}: vscode server arch, e.g. x64, armhf, arm64\n- ${release}: release number", "description": "The URL from where the vscode server will be downloaded. You can use the following variables and they will be replaced dynamically:\n- ${quality}: vscode server quality, e.g. stable or insiders\n- ${version}: vscode server version, e.g. 1.69.0\n- ${commit}: vscode server release commit\n- ${arch}: vscode server arch, e.g. x64, armhf, arm64\n- ${release}: release number",
"scope": "application", "scope": "application",
"default": "https://github.com/voideditor/binaries/releases/download/${version}.${release}/void-reh-${os}-${arch}-${version}.${release}.tar.gz" "default": "https://github.com/voideditor/binaries/releases/download/${version}/void-reh-${os}-${arch}-${version}.tar.gz"
}, },
"remote.SSH.remotePlatform": { "remote.SSH.remotePlatform": {
"type": "object", "type": "object",

View file

@ -8,203 +8,203 @@ import { getVSCodeServerConfig } from './serverConfig';
import SSHConnection from './ssh/sshConnection'; import SSHConnection from './ssh/sshConnection';
export interface ServerInstallOptions { export interface ServerInstallOptions {
id: string; id: string;
quality: string; quality: string;
commit: string; commit: string;
version: string; version: string;
release?: string; // void specific release?: string; // void specific
extensionIds: string[]; extensionIds: string[];
envVariables: string[]; envVariables: string[];
useSocketPath: boolean; useSocketPath: boolean;
serverApplicationName: string; serverApplicationName: string;
serverDataFolderName: string; serverDataFolderName: string;
serverDownloadUrlTemplate: string; serverDownloadUrlTemplate: string;
} }
export interface ServerInstallResult { export interface ServerInstallResult {
exitCode: number; exitCode: number;
listeningOn: number | string; listeningOn: number | string;
connectionToken: string; connectionToken: string;
logFile: string; logFile: string;
osReleaseId: string; osReleaseId: string;
arch: string; arch: string;
platform: string; platform: string;
tmpDir: string; tmpDir: string;
[key: string]: any; [key: string]: any;
} }
export class ServerInstallError extends Error { export class ServerInstallError extends Error {
constructor(message: string) { constructor(message: string) {
super(message); super(message);
} }
} }
const DEFAULT_DOWNLOAD_URL_TEMPLATE = 'https://github.com/voideditor/binaries/releases/download/${version}.${release}/void-reh-${os}-${arch}-${version}.${release}.tar.gz'; const DEFAULT_DOWNLOAD_URL_TEMPLATE = 'https://github.com/voideditor/binaries/releases/download/${version}/void-reh-${os}-${arch}-${version}.tar.gz';
export async function installCodeServer(conn: SSHConnection, serverDownloadUrlTemplate: string | undefined, extensionIds: string[], envVariables: string[], platform: string | undefined, useSocketPath: boolean, logger: Log): Promise<ServerInstallResult> { export async function installCodeServer(conn: SSHConnection, serverDownloadUrlTemplate: string | undefined, extensionIds: string[], envVariables: string[], platform: string | undefined, useSocketPath: boolean, logger: Log): Promise<ServerInstallResult> {
let shell = 'powershell'; let shell = 'powershell';
// detect platform and shell for windows // detect platform and shell for windows
if (!platform || platform === 'windows') { if (!platform || platform === 'windows') {
const result = await conn.exec('uname -s'); const result = await conn.exec('uname -s');
if (result.stdout) { if (result.stdout) {
if (result.stdout.includes('windows32')) { if (result.stdout.includes('windows32')) {
platform = 'windows'; platform = 'windows';
} else if (result.stdout.includes('MINGW64')) { } else if (result.stdout.includes('MINGW64')) {
platform = 'windows'; platform = 'windows';
shell = 'bash'; shell = 'bash';
} }
} else if (result.stderr) { } else if (result.stderr) {
if (result.stderr.includes('FullyQualifiedErrorId : CommandNotFoundException')) { if (result.stderr.includes('FullyQualifiedErrorId : CommandNotFoundException')) {
platform = 'windows'; platform = 'windows';
} }
if (result.stderr.includes('is not recognized as an internal or external command')) { if (result.stderr.includes('is not recognized as an internal or external command')) {
platform = 'windows'; platform = 'windows';
shell = 'cmd'; shell = 'cmd';
} }
} }
if (platform) { if (platform) {
logger.trace(`Detected platform: ${platform}, ${shell}`); logger.trace(`Detected platform: ${platform}, ${shell}`);
} }
} }
const scriptId = crypto.randomBytes(12).toString('hex'); const scriptId = crypto.randomBytes(12).toString('hex');
const vscodeServerConfig = await getVSCodeServerConfig(); const vscodeServerConfig = await getVSCodeServerConfig();
const installOptions: ServerInstallOptions = { const installOptions: ServerInstallOptions = {
id: scriptId, id: scriptId,
version: vscodeServerConfig.version, version: vscodeServerConfig.version,
commit: vscodeServerConfig.commit, commit: vscodeServerConfig.commit,
quality: vscodeServerConfig.quality, quality: vscodeServerConfig.quality,
release: vscodeServerConfig.release, release: vscodeServerConfig.release,
extensionIds, extensionIds,
envVariables, envVariables,
useSocketPath, useSocketPath,
serverApplicationName: vscodeServerConfig.serverApplicationName, serverApplicationName: vscodeServerConfig.serverApplicationName,
serverDataFolderName: vscodeServerConfig.serverDataFolderName, serverDataFolderName: vscodeServerConfig.serverDataFolderName,
serverDownloadUrlTemplate: serverDownloadUrlTemplate ?? vscodeServerConfig.serverDownloadUrlTemplate ?? DEFAULT_DOWNLOAD_URL_TEMPLATE, serverDownloadUrlTemplate: serverDownloadUrlTemplate ?? vscodeServerConfig.serverDownloadUrlTemplate ?? DEFAULT_DOWNLOAD_URL_TEMPLATE,
}; };
let commandOutput: { stdout: string; stderr: string }; let commandOutput: { stdout: string; stderr: string };
if (platform === 'windows') { if (platform === 'windows') {
const installServerScript = generatePowerShellInstallScript(installOptions); const installServerScript = generatePowerShellInstallScript(installOptions);
logger.trace('Server install command:', installServerScript); logger.trace('Server install command:', installServerScript);
const installDir = `$HOME\\${vscodeServerConfig.serverDataFolderName}\\install`; const installDir = `$HOME\\${vscodeServerConfig.serverDataFolderName}\\install`;
const installScript = `${installDir}\\${vscodeServerConfig.commit}.ps1`; const installScript = `${installDir}\\${vscodeServerConfig.commit}.ps1`;
const endRegex = new RegExp(`${scriptId}: end`); const endRegex = new RegExp(`${scriptId}: end`);
// investigate if it's possible to use `-EncodedCommand` flag // investigate if it's possible to use `-EncodedCommand` flag
// https://devblogs.microsoft.com/powershell/invoking-powershell-with-complex-expressions-using-scriptblocks/ // https://devblogs.microsoft.com/powershell/invoking-powershell-with-complex-expressions-using-scriptblocks/
let command = ''; let command = '';
if (shell === 'powershell') { if (shell === 'powershell') {
command = `md -Force ${installDir}; echo @'\n${installServerScript}\n'@ | Set-Content ${installScript}; powershell -ExecutionPolicy ByPass -File "${installScript}"`; command = `md -Force ${installDir}; echo @'\n${installServerScript}\n'@ | Set-Content ${installScript}; powershell -ExecutionPolicy ByPass -File "${installScript}"`;
} else if (shell === 'bash') { } else if (shell === 'bash') {
command = `mkdir -p ${installDir.replace(/\\/g, '/')} && echo '\n${installServerScript.replace(/'/g, '\'"\'"\'')}\n' > ${installScript.replace(/\\/g, '/')} && powershell -ExecutionPolicy ByPass -File "${installScript}"`; command = `mkdir -p ${installDir.replace(/\\/g, '/')} && echo '\n${installServerScript.replace(/'/g, '\'"\'"\'')}\n' > ${installScript.replace(/\\/g, '/')} && powershell -ExecutionPolicy ByPass -File "${installScript}"`;
} else if (shell === 'cmd') { } else if (shell === 'cmd') {
const script = installServerScript.trim() const script = installServerScript.trim()
// remove comments // remove comments
.replace(/^#.*$/gm, '') .replace(/^#.*$/gm, '')
// remove empty lines // remove empty lines
.replace(/\n{2,}/gm, '\n') .replace(/\n{2,}/gm, '\n')
// remove leading spaces // remove leading spaces
.replace(/^\s*/gm, '') .replace(/^\s*/gm, '')
// escape double quotes (from powershell/cmd) // escape double quotes (from powershell/cmd)
.replace(/"/g, '"""') .replace(/"/g, '"""')
// escape single quotes (from cmd) // escape single quotes (from cmd)
.replace(/'/g, `''`) .replace(/'/g, `''`)
// escape redirect (from cmd) // escape redirect (from cmd)
.replace(/>/g, `^>`) .replace(/>/g, `^>`)
// escape new lines (from powershell/cmd) // escape new lines (from powershell/cmd)
.replace(/\n/g, '\'`n\''); .replace(/\n/g, '\'`n\'');
command = `powershell "md -Force ${installDir}" && powershell "echo '${script}'" > ${installScript.replace('$HOME', '%USERPROFILE%')} && powershell -ExecutionPolicy ByPass -File "${installScript.replace('$HOME', '%USERPROFILE%')}"`; command = `powershell "md -Force ${installDir}" && powershell "echo '${script}'" > ${installScript.replace('$HOME', '%USERPROFILE%')} && powershell -ExecutionPolicy ByPass -File "${installScript.replace('$HOME', '%USERPROFILE%')}"`;
logger.trace('Command length (8191 max):', command.length); logger.trace('Command length (8191 max):', command.length);
if (command.length > 8191) { if (command.length > 8191) {
throw new ServerInstallError(`Command line too long`); throw new ServerInstallError(`Command line too long`);
} }
} else { } else {
throw new ServerInstallError(`Not supported shell: ${shell}`); throw new ServerInstallError(`Not supported shell: ${shell}`);
} }
commandOutput = await conn.execPartial(command, (stdout: string) => endRegex.test(stdout)); commandOutput = await conn.execPartial(command, (stdout: string) => endRegex.test(stdout));
} else { } else {
const installServerScript = generateBashInstallScript(installOptions); const installServerScript = generateBashInstallScript(installOptions);
logger.trace('Server install command:', installServerScript); logger.trace('Server install command:', installServerScript);
// Fish shell does not support heredoc so let's workaround it using -c option, // Fish shell does not support heredoc so let's workaround it using -c option,
// also replace single quotes (') within the script with ('\'') as there's no quoting within single quotes, see https://unix.stackexchange.com/a/24676 // also replace single quotes (') within the script with ('\'') as there's no quoting within single quotes, see https://unix.stackexchange.com/a/24676
commandOutput = await conn.exec(`bash -c '${installServerScript.replace(/'/g, `'\\''`)}'`); commandOutput = await conn.exec(`bash -c '${installServerScript.replace(/'/g, `'\\''`)}'`);
} }
if (commandOutput.stderr) { if (commandOutput.stderr) {
logger.trace('Server install command stderr:', commandOutput.stderr); logger.trace('Server install command stderr:', commandOutput.stderr);
} }
logger.trace('Server install command stdout:', commandOutput.stdout); logger.trace('Server install command stdout:', commandOutput.stdout);
const resultMap = parseServerInstallOutput(commandOutput.stdout, scriptId); const resultMap = parseServerInstallOutput(commandOutput.stdout, scriptId);
if (!resultMap) { if (!resultMap) {
throw new ServerInstallError(`Failed parsing install script output`); throw new ServerInstallError(`Failed parsing install script output`);
} }
const exitCode = parseInt(resultMap.exitCode, 10); const exitCode = parseInt(resultMap.exitCode, 10);
if (exitCode !== 0) { if (exitCode !== 0) {
throw new ServerInstallError(`Couldn't install vscode server on remote server, install script returned non-zero exit status`); throw new ServerInstallError(`Couldn't install vscode server on remote server, install script returned non-zero exit status`);
} }
const listeningOn = resultMap.listeningOn.match(/^\d+$/) const listeningOn = resultMap.listeningOn.match(/^\d+$/)
? parseInt(resultMap.listeningOn, 10) ? parseInt(resultMap.listeningOn, 10)
: resultMap.listeningOn; : resultMap.listeningOn;
const remoteEnvVars = Object.fromEntries(Object.entries(resultMap).filter(([key,]) => envVariables.includes(key))); const remoteEnvVars = Object.fromEntries(Object.entries(resultMap).filter(([key,]) => envVariables.includes(key)));
return { return {
exitCode, exitCode,
listeningOn, listeningOn,
connectionToken: resultMap.connectionToken, connectionToken: resultMap.connectionToken,
logFile: resultMap.logFile, logFile: resultMap.logFile,
osReleaseId: resultMap.osReleaseId, osReleaseId: resultMap.osReleaseId,
arch: resultMap.arch, arch: resultMap.arch,
platform: resultMap.platform, platform: resultMap.platform,
tmpDir: resultMap.tmpDir, tmpDir: resultMap.tmpDir,
...remoteEnvVars ...remoteEnvVars
}; };
} }
function parseServerInstallOutput(str: string, scriptId: string): { [k: string]: string } | undefined { function parseServerInstallOutput(str: string, scriptId: string): { [k: string]: string } | undefined {
const startResultStr = `${scriptId}: start`; const startResultStr = `${scriptId}: start`;
const endResultStr = `${scriptId}: end`; const endResultStr = `${scriptId}: end`;
const startResultIdx = str.indexOf(startResultStr); const startResultIdx = str.indexOf(startResultStr);
if (startResultIdx < 0) { if (startResultIdx < 0) {
return undefined; return undefined;
} }
const endResultIdx = str.indexOf(endResultStr, startResultIdx + startResultStr.length); const endResultIdx = str.indexOf(endResultStr, startResultIdx + startResultStr.length);
if (endResultIdx < 0) { if (endResultIdx < 0) {
return undefined; return undefined;
} }
const installResult = str.substring(startResultIdx + startResultStr.length, endResultIdx); const installResult = str.substring(startResultIdx + startResultStr.length, endResultIdx);
const resultMap: { [k: string]: string } = {}; const resultMap: { [k: string]: string } = {};
const resultArr = installResult.split(/\r?\n/); const resultArr = installResult.split(/\r?\n/);
for (const line of resultArr) { for (const line of resultArr) {
const [key, value] = line.split('=='); const [key, value] = line.split('==');
resultMap[key] = value; resultMap[key] = value;
} }
return resultMap; return resultMap;
} }
function generateBashInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate }: ServerInstallOptions) { function generateBashInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate }: ServerInstallOptions) {
const extensions = extensionIds.map(id => '--install-extension ' + id).join(' '); const extensions = extensionIds.map(id => '--install-extension ' + id).join(' ');
return ` return `
# Server installation script # Server installation script
TMP_DIR="\${XDG_RUNTIME_DIR:-"/tmp"}" TMP_DIR="\${XDG_RUNTIME_DIR:-"/tmp"}"
@ -427,16 +427,16 @@ print_install_results_and_exit 0
} }
function generatePowerShellInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate }: ServerInstallOptions) { function generatePowerShellInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate }: ServerInstallOptions) {
const extensions = extensionIds.map(id => '--install-extension ' + id).join(' '); const extensions = extensionIds.map(id => '--install-extension ' + id).join(' ');
const downloadUrl = serverDownloadUrlTemplate const downloadUrl = serverDownloadUrlTemplate
.replace(/\$\{quality\}/g, quality) .replace(/\$\{quality\}/g, quality)
.replace(/\$\{version\}/g, version) .replace(/\$\{version\}/g, version)
.replace(/\$\{commit\}/g, commit) .replace(/\$\{commit\}/g, commit)
.replace(/\$\{os\}/g, 'win32') .replace(/\$\{os\}/g, 'win32')
.replace(/\$\{arch\}/g, 'x64') .replace(/\$\{arch\}/g, 'x64')
.replace(/\$\{release\}/g, release ?? ''); .replace(/\$\{release\}/g, release ?? '');
return ` return `
# Server installation script # Server installation script
$TMP_DIR="$env:TEMP\\$([System.IO.Path]::GetRandomFileName())" $TMP_DIR="$env:TEMP\\$([System.IO.Path]::GetRandomFileName())"

View file

@ -38,7 +38,7 @@
"type": "string", "type": "string",
"description": "The URL from where the vscode server will be downloaded. You can use the following variables and they will be replaced dynamically:\n- ${quality}: vscode server quality, e.g. stable or insiders\n- ${version}: vscode server version, e.g. 1.69.0\n- ${commit}: vscode server release commit\n- ${arch}: vscode server arch, e.g. x64, armhf, arm64\n- ${release}: release number", "description": "The URL from where the vscode server will be downloaded. You can use the following variables and they will be replaced dynamically:\n- ${quality}: vscode server quality, e.g. stable or insiders\n- ${version}: vscode server version, e.g. 1.69.0\n- ${commit}: vscode server release commit\n- ${arch}: vscode server arch, e.g. x64, armhf, arm64\n- ${release}: release number",
"scope": "application", "scope": "application",
"default": "https://github.com/voideditor/binaries/releases/download/${version}.${release}/void-reh-${os}-${arch}-${version}.${release}.tar.gz" "default": "https://github.com/voideditor/binaries/releases/download/${version}/void-reh-${os}-${arch}-${version}.tar.gz"
} }
} }
}, },

View file

@ -39,7 +39,7 @@ export class ServerInstallError extends Error {
} }
} }
const DEFAULT_DOWNLOAD_URL_TEMPLATE = 'https://github.com/voideditor/binaries/releases/download/${version}.${release}/void-reh-${os}-${arch}-${version}.${release}.tar.gz'; const DEFAULT_DOWNLOAD_URL_TEMPLATE = 'https://github.com/voideditor/binaries/releases/download/${version}/void-reh-${os}-${arch}-${version}.tar.gz';
export async function installCodeServer(wslManager: WSLManager, distroName: string, serverDownloadUrlTemplate: string | undefined, extensionIds: string[], envVariables: string[], logger: Log): Promise<ServerInstallResult> { export async function installCodeServer(wslManager: WSLManager, distroName: string, serverDownloadUrlTemplate: string | undefined, extensionIds: string[], envVariables: string[], logger: Log): Promise<ServerInstallResult> {
const scriptId = crypto.randomBytes(12).toString('hex'); const scriptId = crypto.randomBytes(12).toString('hex');

View file

@ -225,7 +225,7 @@ const PastThreadElement = ({ pastThread, idx, hoveredIdx, setHoveredIdx, isRunni
// data-tooltip-content={`Last modified ${formatTime(new Date(pastThread.lastModified))}`} // data-tooltip-content={`Last modified ${formatTime(new Date(pastThread.lastModified))}`}
// data-tooltip-place='top' // data-tooltip-place='top'
> >
{/* <span>{numMessages}</span> */} <span>{`(${numMessages})`}</span>
{formatDate(new Date(pastThread.lastModified))} {formatDate(new Date(pastThread.lastModified))}
</span> </span>
@ -251,7 +251,6 @@ const PastThreadElement = ({ pastThread, idx, hoveredIdx, setHoveredIdx, isRunni
{/* name */} {/* name */}
<span className="truncate overflow-hidden text-ellipsis">{firstMsg}</span> <span className="truncate overflow-hidden text-ellipsis">{firstMsg}</span>
<span className='opacity-60'>{`(${numMessages})`}</span>
</span> </span>
<div className="flex items-center gap-x-1 opacity-60"> <div className="flex items-center gap-x-1 opacity-60">

View file

@ -185,6 +185,8 @@ const getOptionsAtPath = async (accessor: ReturnType<typeof useAccessor>, path:
const toolsService = accessor.get('IToolsService') const toolsService = accessor.get('IToolsService')
const searchForFilesOrFolders = async (t: string, searchFor: 'files' | 'folders') => { const searchForFilesOrFolders = async (t: string, searchFor: 'files' | 'folders') => {
try { try {
@ -309,6 +311,7 @@ const getOptionsAtPath = async (accessor: ReturnType<typeof useAccessor>, path:
if (generateNextOptionsAtPath) { if (generateNextOptionsAtPath) {
nextOptionsAtPath = await generateNextOptionsAtPath(optionText) nextOptionsAtPath = await generateNextOptionsAtPath(optionText)
} }
else if (path.length === 0 && optionText.trim().length > 0) { // (special case): directly search for both files and folders if optionsPath is empty and there's a search term else if (path.length === 0 && optionText.trim().length > 0) { // (special case): directly search for both files and folders if optionsPath is empty and there's a search term
@ -371,9 +374,10 @@ export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(fun
const [didLoadInitialOptions, setDidLoadInitialOptions] = useState(false); const [didLoadInitialOptions, setDidLoadInitialOptions] = useState(false);
const currentPathRef = useRef<string>(JSON.stringify([])); const currentPathRef = useRef<string>(JSON.stringify([]));
// Show breadcrumbs when we have options loaded AND we're either at root level OR in a subfolder
const areBreadcrumbsShowing = true
// dont show breadcrums if first page and user hasnt typed anything
const isTypingEnabled = true
const isBreadcrumbsShowing = optionPath.length === 0 && !optionText ? false : true
const insertTextAtCursor = (text: string) => { const insertTextAtCursor = (text: string) => {
const textarea = textAreaRef.current; const textarea = textAreaRef.current;
@ -435,7 +439,6 @@ export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(fun
else throw new Error(`Unexpected leafNodeType ${option.leafNodeType}`) else throw new Error(`Unexpected leafNodeType ${option.leafNodeType}`)
chatThreadService.addNewStagingSelection(newSelection) chatThreadService.addNewStagingSelection(newSelection)
console.log('selected', option.uri?.fsPath)
} }
else { else {
@ -513,7 +516,7 @@ export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(fun
}; };
}, []); }, []);
// debounced // debounced, but immediate if text is empty
const onPathTextChange = useCallback((newStr: string) => { const onPathTextChange = useCallback((newStr: string) => {
@ -525,16 +528,24 @@ export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(fun
currentPathRef.current = JSON.stringify(optionPath); currentPathRef.current = JSON.stringify(optionPath);
// Set a new timeout to fetch options after a delay const fetchOptions = async () => {
debounceTimerRef.current = window.setTimeout(async () => {
const newOpts = await getOptionsAtPath(accessor, optionPath, newStr) || []; const newOpts = await getOptionsAtPath(accessor, optionPath, newStr) || [];
if (currentPathRef.current !== JSON.stringify(optionPath)) { return; } if (currentPathRef.current !== JSON.stringify(optionPath)) { return; }
setOptions(newOpts); setOptions(newOpts);
setOptionIdx(0); setOptionIdx(0);
debounceTimerRef.current = null; debounceTimerRef.current = null;
}, 300); };
// If text is empty, run immediately without debouncing
if (newStr.trim() === '') {
fetchOptions();
} else {
// Otherwise, set a new timeout to fetch options after a delay
debounceTimerRef.current = window.setTimeout(fetchOptions, 300);
}
}, [optionPath, accessor]); }, [optionPath, accessor]);
const onMenuKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => { const onMenuKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
const isCommandKeyPressed = e.altKey || e.ctrlKey || e.metaKey; const isCommandKeyPressed = e.altKey || e.ctrlKey || e.metaKey;
@ -588,7 +599,7 @@ export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(fun
// do nothing // do nothing
} }
else { // letter else { // letter
if (areBreadcrumbsShowing) { if (isTypingEnabled) {
onPathTextChange(optionText + e.key) onPathTextChange(optionText + e.key)
} }
} }
@ -803,7 +814,7 @@ export const VoidInputBox2 = forwardRef<HTMLTextAreaElement, InputBox2Props>(fun
onWheel={(e) => e.stopPropagation()} onWheel={(e) => e.stopPropagation()}
> >
{/* Breadcrumbs Header */} {/* Breadcrumbs Header */}
{areBreadcrumbsShowing && <div className="px-2 py-1 text-void-fg-1 bg-void-bg-2-alt border-b border-void-border-3 sticky top-0 bg-void-bg-1 z-10 select-none pointer-events-none"> {isBreadcrumbsShowing && <div className="px-2 py-1 text-void-fg-1 bg-void-bg-2-alt border-b border-void-border-3 sticky top-0 bg-void-bg-1 z-10 select-none pointer-events-none">
{optionText ? {optionText ?
<div className="flex items-center"> <div className="flex items-center">
{/* {optionPath.map((path, index) => ( {/* {optionPath.map((path, index) => (