Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 34 additions & 27 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions src/panels/project_tree_view/ProjectTreeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,20 @@ export class ProjectTreeView implements vscode.WebviewViewProvider {
let projectData: any = {};
projectData['icons'] = {
branch: 'folder',
leaf: 'file',
leaf: 'folder',
open: 'folder-opened',
};
projectData['actions'] = this.projectActions;
projectData['label'] = project.name;
projectData['value'] = { project: project.name };
projectData['subItems'] = [];
projectData['open'] = viewOpen !== undefined ? viewOpen : true;
// Mark the active project (selected for highlight)
if (this.wsConfig.activeProject === project.name) {
projectData['selected'] = true;
}

// Always render children so arrows are visible; interaction is restricted in the webview handler
for (let key in project.buildConfigs) {
projectData.subItems.push(this.generateBuildString(project.name, project.buildConfigs[key]));
}
Expand All @@ -237,7 +242,6 @@ export class ProjectTreeView implements vscode.WebviewViewProvider {
projectData.subItems.push(this.generateTestString(project.name, project.twisterConfigs[key]));
}


if (projectData.subItems.length === 0) {
projectData.subItems.push({
icons: {
Expand Down Expand Up @@ -279,6 +283,7 @@ export class ProjectTreeView implements vscode.WebviewViewProvider {
try {
this.treeData.forEach((element: any) => {
if (element.label in this.wsConfig.projects) {
// Persist open state for all projects so their state is restored when they become active
this.wsConfig.projectStates[element.label].viewOpen = element.open;
element.subItems.forEach((build_element: any) => {
if (build_element.label in this.wsConfig.projects[element.label].buildConfigs) {
Expand Down Expand Up @@ -324,7 +329,7 @@ export class ProjectTreeView implements vscode.WebviewViewProvider {
<script nonce="${nonce}" src="${assetUri('src/panels/project_tree_view/ProjectTreeViewHandler.js')}" type="module"></script>
</head>
<body>
<vscode-tree id="project-tree" indent-guides arrows></vscode-tree>
<vscode-tree id="project-tree" indent-guides arrows></vscode-tree>
${body}
</body>
</html>`;
Expand Down
49 changes: 48 additions & 1 deletion src/panels/project_tree_view/ProjectTreeViewHandler.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,63 @@
(function () {
const vscode = acquireVsCodeApi();
const tree = document.querySelector('#project-tree');
let prevData = [];

window.addEventListener('message', event => {
const message = event.data; // The JSON data our extension sent
tree.data = message;
// Keep a deep copy to restore open states when needed
try {
prevData = JSON.parse(JSON.stringify(message));
Copy link
Preview

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using JSON.parse(JSON.stringify()) for deep cloning is inefficient and can fail with circular references or non-serializable data. Consider using a proper deep clone utility or structured cloning if available.

Suggested change
prevData = JSON.parse(JSON.stringify(message));
prevData = structuredClone(message);

Copilot uses AI. Check for mistakes.

} catch {
Copy link
Preview

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The empty catch block silently ignores errors without logging or handling them. Consider logging the error or providing a more specific fallback behavior to aid in debugging.

Suggested change
} catch {
} catch (err) {
console.error("Failed to deep copy message in project tree view handler:", err);

Copilot uses AI. Check for mistakes.

prevData = message;
}
});

function findProjectItemByName(data, name) {
if (!Array.isArray(data)) {
return undefined;
}
return data.find(item => item && item.value && item.value.project === name);
}

function getActiveProjectName(data) {
if (!Array.isArray(data)) {
return undefined;
}
const active = data.find(item => item && item.selected);
return active && active.value ? active.value.project : undefined;
}

function revertOpenStateIfNeeded(selectedProject) {
const activeProject = getActiveProjectName(prevData);
if (activeProject === selectedProject) {
return false;
}
const currItem = findProjectItemByName(tree.data, selectedProject);
const prevItem = findProjectItemByName(prevData, selectedProject);
if (currItem && prevItem && typeof prevItem.open === 'boolean') {
currItem.open = prevItem.open;
tree.data = [...tree.data];
}
return true;
}

tree.addEventListener('vsc-select', (event) => {
vscode.postMessage({ command: event.detail.value.cmd ? event.detail.value.cmd : "setActive", value: event.detail.value, treeData: tree.data });
const val = event.detail.value;
const isProjectClick = val && val.project && !val.build && !val.runner && !val.test && !val.cmd;
Copy link
Preview

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The boolean logic for detecting project clicks is complex and fragile. Consider extracting this into a named function like 'isProjectSelection(val)' to improve readability and maintainability.

Copilot uses AI. Check for mistakes.

if (isProjectClick) {
const selectedProject = val.project;
if (revertOpenStateIfNeeded(selectedProject)) {
vscode.postMessage({ command: "setActive", value: val, treeData: tree.data });
return;
}
}
vscode.postMessage({ command: val && val.cmd ? val.cmd : "setActive", value: val, treeData: tree.data });
});

// No additional toggle listeners needed; we already revert open-state on project click when switching active.

tree.addEventListener("vsc-run-action", (event) => {
vscode.postMessage({ command: event.detail.actionId, value: event.detail.value, treeData: tree.data });
});
Expand Down
15 changes: 9 additions & 6 deletions src/panels/view.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ vscode-collapsible:hover>vscode-icon {
visibility: visible;
}

/*
/* Keep selection color the same even when unfocused */
vscode-tree {
--vscode-list-inactiveSelectionBackground: var(--vscode-button-background);
--vscode-list-inactiveSelectionForeground: var(--vscode-button-foreground);
--vscode-list-activeSelectionForeground: var(--vscode-button-foreground);
--vscode-list-activeSelectionBackground: var(--vscode-button-background);
--vscode-list-inactiveSelectionBackground: var(--vscode-list-activeSelectionBackground);
--vscode-list-inactiveSelectionForeground: var(--vscode-list-activeSelectionForeground);
}

/* Highlight active project differently */
vscode-tree-item[selected] {
background-color: var(--vscode-list-activeSelectionBackground);
color: var(--vscode-list-activeSelectionForeground);
}
*/

.inlineblock {
display: flex;
Expand Down
1 change: 1 addition & 0 deletions src/project_utilities/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export async function createNewProjectFromSample(wsConfig: WorkspaceConfig) {
loadingQuickPick.busy = true;
loadingQuickPick.enabled = false;
loadingQuickPick.placeholder = "Loading sample projects... Please wait.";
loadingQuickPick.ignoreFocusOut = true;
loadingQuickPick.show();

const samplesDir = await getSamples(wsConfig.activeSetupState);
Expand Down