diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8ab623f2..d59f98ec 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -12,5 +12,25 @@
"editor.formatOnSave": true,
"git.enabled": false,
"terminal.integrated.defaultProfile.windows": "PowerShell",
- "C_Cpp.default.compileCommands": "${workspaceFolder}\\.vscode\\compile_commands.json"
+ "C_Cpp.default.compileCommands": "${workspaceFolder}\\.vscode\\compile_commands.json",
+ "workbench.colorCustomizations": {
+ "activityBar.activeBackground": "#e9e5e0",
+ "activityBar.background": "#e9e5e0",
+ "activityBar.foreground": "#15202b",
+ "activityBar.inactiveForeground": "#15202b99",
+ "activityBarBadge.background": "#7da292",
+ "activityBarBadge.foreground": "#15202b",
+ "commandCenter.border": "#15202b99",
+ "sash.hoverBorder": "#e9e5e0",
+ "statusBar.background": "#d3ccc3",
+ "statusBar.foreground": "#15202b",
+ "statusBarItem.hoverBackground": "#bdb3a6",
+ "statusBarItem.remoteBackground": "#d3ccc3",
+ "statusBarItem.remoteForeground": "#15202b",
+ "titleBar.activeBackground": "#d3ccc3",
+ "titleBar.activeForeground": "#15202b",
+ "titleBar.inactiveBackground": "#d3ccc399",
+ "titleBar.inactiveForeground": "#15202b99"
+ },
+ "peacock.color": "#d3ccc3"
}
diff --git a/README.md b/README.md
index 78162f12..43fa919f 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ IDE for Zephyr automatically detects and works with externally managed Zephyr en
- Automatic detection via `ZEPHYR_BASE` environment variable
- No setup required when using Docker, DevContainers, or pre-configured environments
- Full support for build, flash, and debug operations
-- Configurable warning suppression via `zephyr-ide.suppress-workspace-warning`
+- Configurable warning suppression via `zephyr-ide.suppressWorkspaceWarning`
Ideal for Docker/container workflows, CI/CD pipelines, shared development environments, and pre-installed Zephyr setups.
diff --git a/docs/MANUAL.md b/docs/MANUAL.md
index a57e8927..7da9d467 100644
--- a/docs/MANUAL.md
+++ b/docs/MANUAL.md
@@ -161,7 +161,7 @@ Option 1: Click "Don't Show Again" when the warning appears
Option 2: Manually add to `.vscode/settings.json`:
```json
{
- "zephyr-ide.suppress-workspace-warning": true
+ "zephyr-ide.suppressWorkspaceWarning": true
}
```
@@ -277,7 +277,7 @@ You can also use a code-workspace.json file to help manage projects across diffe
You can have a look at this [sample directory](https://github.com/mylonics/zephyr-ide-sample-project) to also help with getting started with sharing projects.
## Advanced Features
-The Zephyr Menu Config or GUI Config may be run from the active project panel. In the project config panel, by default, a Menu Config option is available. This can be changed to GUI Config by adding `"zephyr-ide.use_gui_config": true` to settings.json. Each debug target may be bound to a custom launch configuration (by default they use "Zephyr IDE: Debug" and "Zephyr IDE: Attach").
+The Zephyr Menu Config or GUI Config may be run from the active project panel. In the project config panel, by default, a Menu Config option is available. This can be changed to GUI Config by adding `"zephyr-ide.useGuiConfig": true` to settings.json. Each debug target may be bound to a custom launch configuration (by default they use "Zephyr IDE: Debug" and "Zephyr IDE: Attach").

@@ -411,7 +411,7 @@ IDE for Zephyr provides the following commands accessible via the command palett
The following settings are available in VS Code settings (File > Preferences > Settings):
-### `zephyr-ide.global_directory`
+### `zephyr-ide.globalDirectory`
- **Type**: String or null
- **Default**: null
- **Description**: Manually specify a global directory for west workspace installation and Zephyr tools. Replaces the deprecated `zephyr-ide.tools_directory`.
@@ -419,29 +419,29 @@ The following settings are available in VS Code settings (File > Preferences > S
### `zephyr-ide.tools_directory`
- **Type**: String or null
- **Default**: null
-- **Deprecated**: Use `zephyr-ide.global_directory` instead. The extension automatically migrates this setting on startup.
+- **Deprecated**: Use `zephyr-ide.globalDirectory` instead. The extension automatically migrates this setting on startup.
-### `zephyr-ide.toolchain_directory`
+### `zephyr-ide.toolchainDirectory`
- **Type**: String or null
- **Default**: null
- **Description**: Manually specify the directory containing Zephyr SDK installations. If not specified, defaults to the `toolchains` subdirectory within the global directory.
-### `zephyr-ide.use_gui_config`
+### `zephyr-ide.useGuiConfig`
- **Type**: Boolean
- **Default**: false
-- **Description**: Display GUI config instead of menu config in Project Tree View
+- **Description**: Use GUI configuration editor instead of terminal-based menuconfig in the Project Tree View.
### `zephyr-ide.westNarrowUpdate`
- **Type**: Boolean
- **Default**: false
-- **Description**: If true, uses 'west update --narrow'. If false, uses 'west update' without --narrow.
+- **Description**: Use 'west update --narrow' to reduce disk usage and download time by fetching only required Git history.
-### `zephyr-ide.suppress-workspace-warning`
+### `zephyr-ide.suppressWorkspaceWarning`
- **Type**: Boolean
- **Default**: false
-- **Description**: If true, suppresses the warning about missing workspace environment variables (ZEPHYR_BASE, ZEPHYR_SDK_INSTALL_DIR).
+- **Description**: Hide the notification about missing ZEPHYR_BASE and ZEPHYR_SDK_INSTALL_DIR environment variables. Enable this if you manage these variables externally.
-### `zephyr-ide.venv-folder`
+### `zephyr-ide.venvFolder`
- **Type**: String or null
- **Default**: null
- **Description**: Manually specify a Python virtual environment folder path. If not specified, defaults to .venv in the workspace setup path.
diff --git a/docs/media/project-page-ui-build-two-column.png b/docs/media/project-page-ui-build-two-column.png
new file mode 100644
index 00000000..bcfef884
Binary files /dev/null and b/docs/media/project-page-ui-build-two-column.png differ
diff --git a/docs/media/project-page-ui-dtc-cmake.png b/docs/media/project-page-ui-dtc-cmake.png
new file mode 100644
index 00000000..be5868fb
Binary files /dev/null and b/docs/media/project-page-ui-dtc-cmake.png differ
diff --git a/docs/media/project-page-ui-dtc-west.png b/docs/media/project-page-ui-dtc-west.png
new file mode 100644
index 00000000..07b0ef5b
Binary files /dev/null and b/docs/media/project-page-ui-dtc-west.png differ
diff --git a/docs/media/project-page-ui-kconfig-west.png b/docs/media/project-page-ui-kconfig-west.png
new file mode 100644
index 00000000..333c89ad
Binary files /dev/null and b/docs/media/project-page-ui-kconfig-west.png differ
diff --git a/docs/src/content/docs/getting-started/external-environments.md b/docs/src/content/docs/getting-started/external-environments.md
index 240c2544..d18e9c70 100644
--- a/docs/src/content/docs/getting-started/external-environments.md
+++ b/docs/src/content/docs/getting-started/external-environments.md
@@ -50,7 +50,7 @@ If you prefer to work without setting `ZEPHYR_BASE` (e.g., using west commands d
**Option 2**: Manually add to `.vscode/settings.json`:
```json
{
- "zephyr-ide.suppress-workspace-warning": true
+ "zephyr-ide.suppressWorkspaceWarning": true
}
```
diff --git a/docs/src/content/docs/reference/configuration.md b/docs/src/content/docs/reference/configuration.md
index ee3f9e08..dc626348 100644
--- a/docs/src/content/docs/reference/configuration.md
+++ b/docs/src/content/docs/reference/configuration.md
@@ -5,7 +5,7 @@ description: All IDE for Zephyr VS Code settings — global directory, toolchain
The following settings are available in VS Code settings (File > Preferences > Settings):
-## `zephyr-ide.global_directory`
+## `zephyr-ide.globalDirectory`
- **Type**: String or null
- **Default**: null
@@ -17,9 +17,9 @@ This setting replaces the deprecated `zephyr-ide.tools_directory`. It controls t
- **Type**: String or null
- **Default**: null
-- **Deprecated**: Use `zephyr-ide.global_directory` instead. The extension automatically migrates this setting on startup.
+- **Deprecated**: Use `zephyr-ide.globalDirectory` instead. The extension automatically migrates this setting on startup.
-## `zephyr-ide.toolchain_directory`
+## `zephyr-ide.toolchainDirectory`
- **Type**: String or null
- **Default**: null
@@ -30,17 +30,17 @@ This setting allows you to use a custom location for SDK installations. The dire
**Example:**
```json
{
- "zephyr-ide.toolchain_directory": "/opt/zephyr-sdks"
+ "zephyr-ide.toolchainDirectory": "/opt/zephyr-sdks"
}
```
With this configuration, the extension will look for SDKs in `/opt/zephyr-sdks/zephyr-sdk-0.17.0`, `/opt/zephyr-sdks/zephyr-sdk-0.17.3`, etc.
-## `zephyr-ide.use_gui_config`
+## `zephyr-ide.useGuiConfig`
- **Type**: Boolean
- **Default**: false
-- **Description**: Display GUI config instead of menu config in Project Tree View
+- **Description**: Use GUI configuration editor instead of terminal-based menuconfig in the Project Tree View.
When enabled, the extension will use the graphical Kconfig interface instead of the text-based menu config when configuring projects.
@@ -48,19 +48,19 @@ When enabled, the extension will use the graphical Kconfig interface instead of
- **Type**: Boolean
- **Default**: false
-- **Description**: If true, uses 'west update --narrow'. If false, uses 'west update' without --narrow.
+- **Description**: Use 'west update --narrow' to reduce disk usage and download time by fetching only required Git history.
The `--narrow` flag tells west to only clone the most recent commit history, which can save disk space and download time. This is useful for CI/CD environments or when you don't need the full git history.
-## `zephyr-ide.suppress-workspace-warning`
+## `zephyr-ide.suppressWorkspaceWarning`
- **Type**: Boolean
- **Default**: false
-- **Description**: If true, suppresses the warning about missing workspace environment variables (ZEPHYR_BASE, ZEPHYR_SDK_INSTALL_DIR).
+- **Description**: Hide the notification about missing ZEPHYR_BASE and ZEPHYR_SDK_INSTALL_DIR environment variables. Enable this if you manage these variables externally.
Use this setting when working with externally managed environments to prevent the extension from showing warnings about missing workspace configuration.
-## `zephyr-ide.venv-folder`
+## `zephyr-ide.venvFolder`
- **Type**: String or null
- **Default**: null
diff --git a/docs/src/content/docs/user-guide/other-features.md b/docs/src/content/docs/user-guide/other-features.md
index 904cbd9d..f6e64bd1 100644
--- a/docs/src/content/docs/user-guide/other-features.md
+++ b/docs/src/content/docs/user-guide/other-features.md
@@ -5,7 +5,7 @@ description: Use MenuConfig, GuiConfig, custom west/CMake build arguments, ROM/R
## Menu Config and GUI Config
-The Zephyr Menu Config or GUI Config may be run from the active project panel. In the project config panel, by default, a Menu Config option is available. This can be changed to GUI Config by adding `"zephyr-ide.use_gui_config": true` to settings.json.
+The Zephyr Menu Config or GUI Config may be run from the active project panel. In the project config panel, by default, a Menu Config option is available. This can be changed to GUI Config by adding `"zephyr-ide.useGuiConfig": true` to settings.json.
Each debug target may be bound to a custom launch configuration (by default they use "Zephyr IDE: Debug" and "Zephyr IDE: Attach").
diff --git a/package.json b/package.json
index abf6db23..2f4746d3 100644
--- a/package.json
+++ b/package.json
@@ -52,24 +52,15 @@
"type": "object",
"title": "Zephyr IDE",
"properties": {
- "zephyr-ide.global_directory": {
- "type": [
- "string",
- "null"
- ],
- "default": null,
- "description": "Manually specify a global directory for west workspace installation and Zephyr tools. The toolchains subdirectory within this folder is used by default for SDK installations unless overridden by 'zephyr-ide.toolchain_directory'."
- },
- "zephyr-ide.tools_directory": {
+ "zephyr-ide.globalDirectory": {
"type": [
"string",
"null"
],
"default": null,
- "deprecationMessage": "Deprecated: Use 'zephyr-ide.global_directory' instead. The extension will automatically migrate this setting on startup.",
- "description": "Deprecated. Use 'zephyr-ide.global_directory' instead."
+ "description": "Manually specify a global directory for west workspace installation and Zephyr tools. The toolchains subdirectory within this folder is used by default for SDK installations unless overridden by 'zephyr-ide.toolchainDirectory'."
},
- "zephyr-ide.toolchain_directory": {
+ "zephyr-ide.toolchainDirectory": {
"type": [
"string",
"null"
@@ -77,28 +68,28 @@
"default": null,
"description": "Manually specify the directory containing Zephyr SDK installations (e.g., containing zephyr-sdk-0.17.0, zephyr-sdk-0.17.3 subdirectories). If not specified, defaults to toolchains subdirectory within the tools directory."
},
- "zephyr-ide.use_gui_config": {
+ "zephyr-ide.useGuiConfig": {
"type": [
"boolean"
],
"default": false,
- "description": "Display gui config instead of menu config in Project Tree View"
+ "description": "Use GUI configuration editor instead of terminal-based menuconfig in the Project Tree View."
},
"zephyr-ide.westNarrowUpdate": {
"type": [
"boolean"
],
"default": false,
- "description": "If true, uses 'west update --narrow'. If false, uses 'west update' without --narrow."
+ "description": "Use 'west update --narrow' to reduce disk usage and download time by fetching only required Git history."
},
- "zephyr-ide.suppress-workspace-warning": {
+ "zephyr-ide.suppressWorkspaceWarning": {
"type": [
"boolean"
],
"default": false,
- "description": "If true, suppresses the warning about missing workspace environment variables (ZEPHYR_BASE, ZEPHYR_SDK_INSTALL_DIR)."
+ "description": "Hide the notification about missing ZEPHYR_BASE and ZEPHYR_SDK_INSTALL_DIR environment variables. Enable this if you manage these variables externally."
},
- "zephyr-ide.venv-folder": {
+ "zephyr-ide.venvFolder": {
"type": [
"string",
"null"
@@ -113,7 +104,7 @@
"default": true,
"description": "Automatically switch the active project when the editor focus changes to a file belonging to a different project."
},
- "zephyr-ide.project_variable_defaults": {
+ "zephyr-ide.projectVariableDefaults": {
"type": [
"array"
],
@@ -123,7 +114,7 @@
},
"description": "Default project variable names to show in the Project Details panel. Missing values are shown as empty."
},
- "zephyr-ide.build_variable_defaults": {
+ "zephyr-ide.buildVariableDefaults": {
"type": [
"array"
],
@@ -132,6 +123,80 @@
"type": "string"
},
"description": "Default build variable names to show in the Project Details panel. Missing values are shown as empty."
+ },
+ "zephyr-ide.global_directory": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": null,
+ "deprecationMessage": "Deprecated: Use 'zephyr-ide.globalDirectory' instead. Migrated automatically on startup.",
+ "description": "Deprecated. Use 'zephyr-ide.globalDirectory' instead."
+ },
+ "zephyr-ide.tools_directory": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": null,
+ "deprecationMessage": "Deprecated: Use 'zephyr-ide.globalDirectory' instead. Migrated automatically on startup.",
+ "description": "Deprecated. Use 'zephyr-ide.globalDirectory' instead."
+ },
+ "zephyr-ide.toolchain_directory": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": null,
+ "deprecationMessage": "Deprecated: Use 'zephyr-ide.toolchainDirectory' instead. Migrated automatically on startup.",
+ "description": "Deprecated. Use 'zephyr-ide.toolchainDirectory' instead."
+ },
+ "zephyr-ide.use_gui_config": {
+ "type": [
+ "boolean"
+ ],
+ "default": false,
+ "deprecationMessage": "Deprecated: Use 'zephyr-ide.useGuiConfig' instead. Migrated automatically on startup.",
+ "description": "Deprecated. Use 'zephyr-ide.useGuiConfig' instead."
+ },
+ "zephyr-ide.suppress-workspace-warning": {
+ "type": [
+ "boolean"
+ ],
+ "default": false,
+ "deprecationMessage": "Deprecated: Use 'zephyr-ide.suppressWorkspaceWarning' instead. Migrated automatically on startup.",
+ "description": "Deprecated. Use 'zephyr-ide.suppressWorkspaceWarning' instead."
+ },
+ "zephyr-ide.venv-folder": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": null,
+ "deprecationMessage": "Deprecated: Use 'zephyr-ide.venvFolder' instead. Migrated automatically on startup.",
+ "description": "Deprecated. Use 'zephyr-ide.venvFolder' instead."
+ },
+ "zephyr-ide.project_variable_defaults": {
+ "type": [
+ "array"
+ ],
+ "default": [],
+ "items": {
+ "type": "string"
+ },
+ "deprecationMessage": "Deprecated: Use 'zephyr-ide.projectVariableDefaults' instead. Migrated automatically on startup.",
+ "description": "Deprecated. Use 'zephyr-ide.projectVariableDefaults' instead."
+ },
+ "zephyr-ide.build_variable_defaults": {
+ "type": [
+ "array"
+ ],
+ "default": [],
+ "items": {
+ "type": "string"
+ },
+ "deprecationMessage": "Deprecated: Use 'zephyr-ide.buildVariableDefaults' instead. Migrated automatically on startup.",
+ "description": "Deprecated. Use 'zephyr-ide.buildVariableDefaults' instead."
}
}
},
@@ -183,6 +248,11 @@
"group": "navigation",
"when": "view == zephyrIdeWestWorkspaces"
},
+ {
+ "command": "zephyr-ide.config-view.open-project-details",
+ "group": "navigation",
+ "when": "view == zephyrIdeProjectStatus"
+ },
{
"command": "zephyr-ide.disable-automatic-project-target",
"when": "view == zephyrIdeActiveProject"
@@ -309,7 +379,7 @@
"zephyr-ide-main": [
{
"id": "zephyrIdeExtensionSetup",
- "name": "Zephyr IDE and Workspace Setup",
+ "name": "Extension Setup",
"icon": "media/logo_small.jpg",
"contextualTitle": "Zephyr IDE"
},
@@ -327,9 +397,9 @@
},
{
"id": "zephyrIdeProjectStatus",
- "name": "Project Config",
+ "name": "Project Details",
"icon": "media/logo_small.jpg",
- "contextualTitle": "Zephyr IDE Projects"
+ "contextualTitle": "Zephyr IDE Project Details"
},
{
"id": "zephyrIdeActiveProject",
@@ -346,15 +416,15 @@
},
{
"command": "zephyr-ide.print-workspace",
- "title": "Zephyr IDE: Print Workspace Structure"
+ "title": "Zephyr IDE: Show Workspace Structure"
},
{
"command": "zephyr-ide.print-python-path",
- "title": "Zephyr IDE: Print Python Interpreter Path"
+ "title": "Zephyr IDE: Show Python Interpreter Path"
},
{
"command": "zephyr-ide.check-build-dependencies",
- "title": "Zephyr IDE: Setup Check Build Dependencies Available"
+ "title": "Zephyr IDE: Check Build Dependencies"
},
{
"command": "zephyr-ide.setup-west-environment",
@@ -424,11 +494,11 @@
},
{
"command": "zephyr-ide.add-project-config-files",
- "title": "Zephyr IDE: Add Project KConfig Files"
+ "title": "Zephyr IDE: Add Project Kconfig Files"
},
{
"command": "zephyr-ide.remove-project-config-files",
- "title": "Zephyr IDE: Remove Project KConfig Files"
+ "title": "Zephyr IDE: Remove Project Kconfig Files"
},
{
"command": "zephyr-ide.add-project-overlay-files",
@@ -452,11 +522,11 @@
},
{
"command": "zephyr-ide.add-build-config-files",
- "title": "Zephyr IDE: Add Build KConfig Files"
+ "title": "Zephyr IDE: Add Build Kconfig Files"
},
{
"command": "zephyr-ide.remove-build-config-files",
- "title": "Zephyr IDE: Remove Build KConfig Files"
+ "title": "Zephyr IDE: Remove Build Kconfig Files"
},
{
"command": "zephyr-ide.add-build-overlay-files",
@@ -548,7 +618,15 @@
},
{
"command": "zephyr-ide.debug-internal-shell",
- "title": "Zephyr IDE: Debug Internal Shell"
+ "title": "Zephyr IDE: Open Debug Shell"
+ },
+ {
+ "command": "zephyr-ide.debug-reset-host-tools",
+ "title": "Zephyr IDE: Extension Debug — Mark Host Tools Not Ready"
+ },
+ {
+ "command": "zephyr-ide.debug-reset-sdk",
+ "title": "Zephyr IDE: Extension Debug — Mark SDK Not Installed"
},
{
"command": "zephyr-ide.west-list",
@@ -556,11 +634,11 @@
},
{
"command": "zephyr-ide.update-web-view",
- "title": "Zephyr IDE: Reload Web Views"
+ "title": "Zephyr IDE: Reload Panels"
},
{
"command": "zephyr-ide.reset-zephyr-install-selection",
- "title": "Zephyr IDE: Reset Zephyr Install Selection"
+ "title": "Zephyr IDE: Reset Active Installation"
},
{
"command": "zephyr-ide.manage-workspaces",
@@ -568,7 +646,7 @@
},
{
"command": "zephyr-ide.mark-west-as-ready",
- "title": "Zephyr IDE: Mark West as Ready"
+ "title": "Zephyr IDE: Skip West Setup"
},
{
"command": "zephyr-ide.shell_test",
@@ -612,11 +690,19 @@
},
{
"command": "zephyr-ide.open-setup-panel",
- "title": "Zephyr IDE: Open Setup Panel"
+ "title": "Zephyr IDE: Overview"
+ },
+ {
+ "command": "zephyr-ide.open-setup-panel-workspace",
+ "title": "Zephyr IDE: Open Workspace Config"
+ },
+ {
+ "command": "zephyr-ide.open-setup-panel-sdk",
+ "title": "Zephyr IDE: Open SDK Management"
},
{
"command": "zephyr-ide.open-project-build-panel",
- "title": "Zephyr IDE: Project/Build Details"
+ "title": "Zephyr IDE: Project Details"
},
{
"command": "zephyr-ide.open-settings-panel",
@@ -624,7 +710,15 @@
},
{
"command": "zephyr-ide.open-host-tools-panel",
- "title": "Zephyr IDE: Host Tools Installation"
+ "title": "Zephyr IDE: Host Tools"
+ },
+ {
+ "command": "zephyr-ide.open-sdk-panel",
+ "title": "Zephyr IDE: SDK Management"
+ },
+ {
+ "command": "zephyr-ide.open-workspace-panel",
+ "title": "Zephyr IDE: Workspace Config"
},
{
"command": "zephyr-ide.workspace-setup-picker",
@@ -741,7 +835,7 @@
},
{
"command": "zephyr-ide.west-workspace.deselect",
- "title": "Deselect Workspace",
+ "title": "Deactivate Workspace",
"icon": "$(close)"
},
{
@@ -771,6 +865,10 @@
"command": "zephyr-ide.config-view.open-cmake",
"title": "Open CMakeLists"
},
+ {
+ "command": "zephyr-ide.config-view.open-project-details",
+ "title": "Open Project Details"
+ },
{
"command": "zephyr-ide.config-view.modify-build-args",
"title": "Modify Build Args"
@@ -1025,7 +1123,7 @@
"scripts": {
"vscode:prepublish": "npm run esbuild-base -- --minify && npm run esbuild-webview-base -- --minify",
"esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node",
- "esbuild-webview-base": "esbuild ./src/panels/host_tool_install_view/host-tool-install.ts ./src/panels/setup_panel/setup-panel.ts ./src/panels/project_build_view/project-build-panel.ts ./src/panels/settings_view/settings-panel.ts --bundle --outdir=dist/webview --outbase=src/panels --format=iife --platform=browser",
+ "esbuild-webview-base": "esbuild ./src/panels/host_tool_install_view/host-tool-install.ts ./src/panels/setup_panel/setup-panel.ts ./src/panels/project_build_view/project-build-panel.ts ./src/panels/settings_view/settings-panel.ts ./src/panels/sdk_panel/sdk-panel.ts ./src/panels/workspace_panel/workspace-panel.ts --bundle --outdir=dist/webview --outbase=src/panels --format=iife --platform=browser",
"esbuild": "npm run esbuild-base -- --sourcemap && npm run esbuild-webview-base -- --sourcemap",
"esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
"test-compile": "tsc -p ./ && tsc -p ./tsconfig.webview.json",
diff --git a/src/defines.ts b/src/defines.ts
index cecf846d..6c11afd4 100644
--- a/src/defines.ts
+++ b/src/defines.ts
@@ -61,8 +61,8 @@ export const zephyrVersions = ["v4.3.0", "v4.2.0", "main", "Other Version"];
export const ncsVersions = ["v3.2.2", "v3.2.1", "v3.1.1", "main", "Other Version"];
export const sdkVersions = [
- { label: "0.17.3", description: "Zephyr 4.2+" },
- { label: "0.17.0", description: "Zephyr 4.1-4.0" },
+ { label: "0.17.4", description: "Zephyr 4.3" },
+ { label: "0.17.3", description: "Zephyr 4.2" },
{ label: "0.16.9", description: "Zephyr 3.7" },
{ label: "sep", kind: vscode.QuickPickItemKind.Separator },
{ label: "latest", description: "Latest available version" },
diff --git a/src/extension.ts b/src/extension.ts
index e95ea120..8a885378 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -28,6 +28,8 @@ import { SetupPanel } from "./panels/setup_panel/SetupPanel";
import { HostToolInstallView } from "./panels/host_tool_install_view/HostToolInstallView";
import { ProjectBuildPanel } from "./panels/project_build_view/ProjectBuildPanel";
import { SettingsPanel } from "./panels/settings_view/SettingsPanel";
+import { SDKPanel } from "./panels/sdk_panel/SDKPanel";
+import { WorkspacePanel } from "./panels/workspace_panel/WorkspacePanel";
import {
output,
@@ -68,7 +70,7 @@ import {
loadProjectsFromFile,
getToolchainDir,
getToolchainPath,
- migrateToolsDirectory,
+ migrateSettingKeys,
setWorkspaceSettings,
getSetupState,
getGdbPath,
@@ -126,10 +128,7 @@ async function markWorkspaceSetupComplete(
) {
wsConfig.initialSetupComplete = true;
await setWorkspaceState(context, wsConfig);
- // Update setup panel if it's open
- if (SetupPanel.currentPanel) {
- SetupPanel.currentPanel.updateContent(wsConfig, globalConfig, "workspace");
- }
+ void vscode.commands.executeCommand("zephyr-ide.update-web-view");
}
/** Register a webview view provider with retained context. */
@@ -314,8 +313,8 @@ export async function activate(context: vscode.ExtensionContext) {
const remoteName = vscode.env.remoteName;
outputInfo("Startup", `Platform: ${platformName} (${platformArch})${remoteName ? `, remote: ${remoteName}` : ""}${isWSL() ? " [WSL]" : ""}`);
- // Migrate deprecated tools_directory setting to global_directory
- await migrateToolsDirectory();
+ // Migrate deprecated setting keys to camelCase equivalents
+ await migrateSettingKeys();
wsConfig = await loadWorkspaceState(context);
globalConfig = await loadGlobalState(context);
@@ -499,6 +498,15 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand("zephyr-ide.config-view.delete-file", (item: any) => {
projectConfigView.handleDeleteFile(item);
}),
+ vscode.commands.registerCommand("zephyr-ide.config-view.open-project-details", () => {
+ ProjectBuildPanel.createOrShow(
+ context.extensionPath,
+ context,
+ wsConfig,
+ globalConfig,
+ wsConfig.activeProject,
+ );
+ }),
);
// -- ProjectTreeView inline action commands --
@@ -559,12 +567,12 @@ export async function activate(context: vscode.ExtensionContext) {
() => project.setActiveRunner(context, wsConfig));
activeProjectDisplay = createStatusBarButton(context,
- "zephyr-ide.set-active-project", `$(folder) ${wsConfig.activeProject}`, "Zephyr IDE Active Project");
+ "zephyr-ide.set-active-project", `$(folder) ${wsConfig.activeProject}`, "Zephyr IDE Select Active Project");
activeBuildDisplay = createStatusBarButton(context,
- "zephyr-ide.set-active-build", ``, "Zephyr IDE Active Build");
+ "zephyr-ide.set-active-build", ``, "Select Active Build");
activeRunnerDisplay = createStatusBarButton(context,
- "zephyr-ide.set-active-runner", ``, "Zephyr IDE Active Runner");
+ "zephyr-ide.set-active-runner", ``, "Select Active Runner");
{
refreshStatusBar();
}
@@ -622,7 +630,7 @@ export async function activate(context: vscode.ExtensionContext) {
await setupWestEnvironment(context, wsConfig, globalConfig);
extensionSetupView.updateWebView(wsConfig, globalConfig);
} else {
- notifyError("West Environment", "Open Folder or Setup Workspace Before Continuing");
+ notifyError("West Environment", "Open a folder or set up a workspace first");
}
}
)
@@ -654,18 +662,14 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand("zephyr-ide.reset-workspace", async () => {
await clearWorkspaceState(context, wsConfig);
- extensionSetupView.updateWebView(wsConfig, globalConfig);
- // Also update setup panel if it's open
- if (SetupPanel.currentPanel) {
- SetupPanel.currentPanel.updateContent(wsConfig, globalConfig);
- }
+ void vscode.commands.executeCommand("zephyr-ide.update-web-view");
})
);
context.subscriptions.push(
vscode.commands.registerCommand("zephyr-ide.clear-projects", async () => {
const selection = await vscode.window.showWarningMessage(
- "Are you sure you want to Clear All Projects?",
+ "Are you sure you want to clear all projects?",
"Yes",
"Cancel"
);
@@ -1100,6 +1104,8 @@ export async function activate(context: vscode.ExtensionContext) {
SetupPanel.currentPanel.updateContent(wsConfig, globalConfig);
}
ProjectBuildPanel.updateAllPanels(wsConfig, globalConfig);
+ SDKPanel.updateAllPanels(wsConfig, globalConfig);
+ WorkspacePanel.updateAllPanels(wsConfig, globalConfig);
void vscode.commands.executeCommand("zephyr-ide.update-status");
})
);
@@ -1184,6 +1190,24 @@ export async function activate(context: vscode.ExtensionContext) {
)
);
+ context.subscriptions.push(
+ vscode.commands.registerCommand("zephyr-ide.debug-reset-host-tools", async () => {
+ globalConfig.toolsAvailable = false;
+ await saveSetupState(context, wsConfig, globalConfig);
+ void vscode.commands.executeCommand("zephyr-ide.update-web-view");
+ void vscode.window.showInformationMessage("Zephyr IDE Debug: Host tools marked as not ready");
+ })
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand("zephyr-ide.debug-reset-sdk", async () => {
+ globalConfig.sdkInstalled = false;
+ await saveSetupState(context, wsConfig, globalConfig);
+ void vscode.commands.executeCommand("zephyr-ide.update-web-view");
+ void vscode.window.showInformationMessage("Zephyr IDE Debug: SDK marked as not installed");
+ })
+ );
+
context.subscriptions.push(
vscode.commands.registerCommand("zephyr-ide.west-list", async () => {
if (!wsConfig.activeSetupState) {
@@ -1260,19 +1284,13 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand("zephyr-ide.install-host-tools", async () => {
- // Open the Setup Panel and navigate to host tools page
- const panel = SetupPanel.createOrShow(
+ // Open the standalone Host Tools panel
+ HostToolInstallView.createOrShow(
context.extensionPath,
context,
wsConfig,
globalConfig
);
- // Navigate to host tools page after a short delay to ensure panel is ready
- setTimeout(() => {
- if (SetupPanel.currentPanel) {
- SetupPanel.currentPanel.navigateToHostTools();
- }
- }, 100);
}
)
);
@@ -1340,6 +1358,30 @@ export async function activate(context: vscode.ExtensionContext) {
)
);
+ context.subscriptions.push(
+ vscode.commands.registerCommand("zephyr-ide.open-setup-panel-workspace", async () => {
+ WorkspacePanel.createOrShow(
+ context.extensionPath,
+ context,
+ wsConfig,
+ globalConfig
+ );
+ }
+ )
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand("zephyr-ide.open-setup-panel-sdk", async () => {
+ SDKPanel.createOrShow(
+ context.extensionPath,
+ context,
+ wsConfig,
+ globalConfig
+ );
+ }
+ )
+ );
+
context.subscriptions.push(
vscode.commands.registerCommand("zephyr-ide.open-project-build-panel", async () => {
ProjectBuildPanel.createOrShow(
@@ -1364,6 +1406,28 @@ export async function activate(context: vscode.ExtensionContext) {
)
);
+ context.subscriptions.push(
+ vscode.commands.registerCommand("zephyr-ide.open-sdk-panel", async () => {
+ SDKPanel.createOrShow(
+ context.extensionPath,
+ context,
+ wsConfig,
+ globalConfig
+ );
+ })
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand("zephyr-ide.open-workspace-panel", async () => {
+ WorkspacePanel.createOrShow(
+ context.extensionPath,
+ context,
+ wsConfig,
+ globalConfig
+ );
+ })
+ );
+
context.subscriptions.push(
vscode.commands.registerCommand("zephyr-ide.open-settings-panel", async () => {
SettingsPanel.createOrShow(context.extensionPath);
diff --git a/src/panels/host_tool_install_view/HostToolInstallView.ts b/src/panels/host_tool_install_view/HostToolInstallView.ts
index 5967fff4..6efcda0d 100644
--- a/src/panels/host_tool_install_view/HostToolInstallView.ts
+++ b/src/panels/host_tool_install_view/HostToolInstallView.ts
@@ -95,7 +95,7 @@ export class HostToolInstallView {
const panel = vscode.window.createWebviewPanel(
"zephyrIDEHostTools",
- "Host Tools Installation",
+ "Zephyr IDE: Host Tools",
column || vscode.ViewColumn.One,
{
enableScripts: true,
@@ -169,6 +169,9 @@ export class HostToolInstallView {
case "hostToolsOpenManagerInstallUrl":
await this._service.openManagerInstallUrl();
break;
+ case "openSetupPanel":
+ vscode.commands.executeCommand("zephyr-ide.open-setup-panel");
+ break;
}
}
@@ -196,6 +199,17 @@ export class HostToolInstallView {
)
);
+ const codiconUri = this._panel.webview.asWebviewUri(
+ vscode.Uri.joinPath(
+ vscode.Uri.file(this._extensionPath),
+ "node_modules",
+ "@vscode",
+ "codicons",
+ "dist",
+ "codicon.css"
+ )
+ );
+
const jsUri = this._panel.webview.asWebviewUri(
vscode.Uri.joinPath(
vscode.Uri.file(this._extensionPath),
@@ -213,9 +227,15 @@ export class HostToolInstallView {
Host Tools Installation
+
+
`;
}
-function buildArgsTableHtml(
- title: string,
+function buildArgsTabContentHtml(
kind: "west" | "cmake",
args: string[],
projectName: string,
buildName: string,
): string {
return `
-
-
-
`;
+}
+
+function tabbedBuildArgsHtml(
+ westArgs: string[],
+ cmakeArgs: string[],
+ projectName: string,
+ buildName: string,
+): string {
+ return `
+
+
+ West Args
+
+ ${buildArgsTabContentHtml("west", westArgs, projectName, buildName)}
+
+ CMake Args
+
+ ${buildArgsTabContentHtml("cmake", cmakeArgs, projectName, buildName)}
+
+
`;
}
@@ -164,9 +131,9 @@ function calculatedSectionHtml(
if (buildInfo) {
buildOutputHtml = `
-
Resolved KConfig Files (from build output)
+
Resolved Kconfig Files (from build output)
${readonlyFileListHtml(buildInfo.kconfigFiles)}
-
Resolved KConfig User Files
+
Resolved Kconfig User Files
${readonlyFileListHtml(buildInfo.otherKconfigFiles)}
Resolved DTS File
${buildInfo.dtsFile ? readonlyFileListHtml([buildInfo.dtsFile]) : '
None
'}
@@ -182,18 +149,14 @@ function calculatedSectionHtml(
}
return `
-
-
-
-
Composed KConfig Files (project + build)
- ${readonlyFileListHtml(calculated.config.concat(calculated.extraConfig))}
-
Composed DTC Overlay Files (project + build)
- ${readonlyFileListHtml(calculated.overlay.concat(calculated.extraOverlay))}
-
-
- ${buildOutputHtml}
-
- `;
+
+
Composed Kconfig Files (project + build)
+ ${readonlyFileListHtml(calculated.config.concat(calculated.extraConfig))}
+
Composed Devicetree Overlay Files (project + build)
+ ${readonlyFileListHtml(calculated.overlay.concat(calculated.extraOverlay))}
+
+
+ ${buildOutputHtml}`;
}
function runnersHtml(
@@ -209,11 +172,22 @@ function runnersHtml(
(r) => `
${escapeHtml(r.name)}
-
Runner: ${escapeHtml(r.config.runner)}
-
Args: ${escapeHtml(r.config.args || "(none)")}
-
-
-
+
+
+
+
+
+
+
`,
)
.join("\n");
@@ -224,124 +198,101 @@ export function getBuildSectionHtml(
projectName: string,
buildName: string,
buildVars: Record
,
- calculated: CalculatedConfigFiles,
- buildInfo: BuildInfo | undefined,
isActive: boolean,
): string {
const activeClass = isActive ? " build-active" : "";
- const activeBadge = isActive ? 'Active ' : "";
+ const activeBadge = isActive ? ' Active ' : "";
return `
-
-
-
${escapeHtml(build.boardDisplayName)}
- ${activeBadge}
+
+
+
+ Board
+ ${escapeHtml(build.boardDisplayName)}
-
-
-
- Board
- ${escapeHtml(build.boardDisplayName)}
-
-
- Board Directory
- ${escapeHtml(build.resolvedBoardPath ?? "Not resolved")}
-
-
-
Debug Optimization
-
${escapeHtml(build.debugOptimization)}
+
+ Board Directory
+ ${escapeHtml(build.resolvedBoardPath ?? "Not resolved")}
+
+
+
+
+ Debug Optimization
+ ${escapeHtml(build.debugOptimization)}
+
- ${buildArgsTableHtml("West Args", "west", build.westBuildArgs, projectName, buildName)}
- ${buildArgsTableHtml("CMake Args", "cmake", build.westBuildCMakeArgs, projectName, buildName)}
+
+
+
+
+ Config
+
+ ${buildVariablesTableHtml(buildVars, projectName, buildName)}
- ${fileGroupHtml(
- "KConfig Files",
- `kconfig-build-${buildName}`,
+ ${tabbedConfigGroupHtml(
+ `build-${buildName}`,
build.confFiles.config,
build.confFiles.extraConfig,
"addBuildConfigFile",
"removeBuildConfigFile",
- )}
-
- ${fileGroupHtml(
- "DTC Overlay Files",
- `overlay-build-${buildName}`,
+ "toggleBuildConfigFileExtra",
build.confFiles.overlay,
build.confFiles.extraOverlay,
"addBuildOverlayFile",
"removeBuildOverlayFile",
+ "toggleBuildOverlayFileExtra",
)}
+
+ Build & Debug
+
+ ${tabbedBuildArgsHtml(build.westBuildArgs, build.westBuildCMakeArgs, projectName, buildName)}
- ${calculatedSectionHtml(calculated, buildInfo)}
+
+
+
+ Debug
+ ${escapeHtml(getLaunchTargetDisplayName(build.launchTarget, build.launchTargetFolder, "Zephyr IDE: Debug"))}
+
+
+
+
+ Build + Debug
+ ${escapeHtml(getLaunchTargetDisplayName(build.buildDebugTarget, build.buildDebugTargetFolder, "Zephyr IDE: Debug"))}
+
+
+
+
+ Attach
+ ${escapeHtml(getLaunchTargetDisplayName(build.attachTarget, build.attachTargetFolder, "Zephyr IDE: Attach"))}
+
+
+
+
- ${buildVariablesTableHtml(buildVars, projectName, buildName)}
-
-
-
-
- Debug
- ${escapeHtml(getLaunchTargetDisplayName(build.launchTarget, build.launchTargetFolder, "Zephyr IDE: Debug"))}
-
-
-
-
-
- Build + Debug
- ${escapeHtml(getLaunchTargetDisplayName(build.buildDebugTarget, build.buildDebugTargetFolder, "Zephyr IDE: Debug"))}
-
-
-
-
-
- Attach
- ${escapeHtml(getLaunchTargetDisplayName(build.attachTarget, build.attachTargetFolder, "Zephyr IDE: Attach"))}
-
-
-
-
-
-
-
-
- ${runnersHtml(build.runners, projectName, buildName)}
-
-
-
-
-
-
-
-
- Build
-
-
-
- Build Pristine
-
-
-
- Flash
-
-
-
- Debug
-
-
-
- Build + Debug
-
-
-
-
+
+
+ ${runnersHtml(build.runners, projectName, buildName)}
+
+
+
-
+
`;
}
+
+export function getCalculatedSectionHtml(
+ calculated: CalculatedConfigFiles,
+ buildInfo: BuildInfo | undefined,
+): string {
+ return calculatedSectionHtml(calculated, buildInfo);
+}
diff --git a/src/panels/project_build_view/ProjectBuildPanel.ts b/src/panels/project_build_view/ProjectBuildPanel.ts
index 4ec46868..3557e183 100644
--- a/src/panels/project_build_view/ProjectBuildPanel.ts
+++ b/src/panels/project_build_view/ProjectBuildPanel.ts
@@ -50,17 +50,18 @@ import {
selectDebugAttachLaunchConfiguration,
getProjectFolder,
} from "../../project_utilities/project";
+import { getConfFileKey } from "../../project_utilities/config_selector";
import { escapeHtml } from "../webview_shared/webviewTypes";
import { generateNonce } from "../webview_shared/nonce";
import { getProjectSectionHtml } from "./ProjectSection";
-import { getBuildSectionHtml } from "./BuildSection";
+import { getBuildSectionHtml, getCalculatedSectionHtml } from "./BuildSection";
import { getTestSectionHtml } from "./TestSection";
import { getVariablesReferenceSectionHtml } from "./VariablesSection";
import { normalizeBuildArgs } from "../../project_utilities/build_args";
export class ProjectBuildPanel {
- private static readonly PROJECT_VARIABLE_DEFAULTS_CONFIG_KEY = "zephyr-ide.project_variable_defaults";
- private static readonly BUILD_VARIABLE_DEFAULTS_CONFIG_KEY = "zephyr-ide.build_variable_defaults";
+ private static readonly PROJECT_VARIABLE_DEFAULTS_CONFIG_KEY = "zephyr-ide.projectVariableDefaults";
+ private static readonly BUILD_VARIABLE_DEFAULTS_CONFIG_KEY = "zephyr-ide.buildVariableDefaults";
/** All open panels, keyed by project name (or "__default__" when no project specified) */
private static _panels: Map
= new Map();
@@ -74,6 +75,7 @@ export class ProjectBuildPanel {
private _globalConfig: GlobalConfig;
private _selectedProject: string | undefined;
private _selectedBuildOrTest: string | undefined; // "build:" or "test:"
+ private _htmlInitialized = false;
/** For backward-compat: returns the first open panel, if any */
public static get currentPanel(): ProjectBuildPanel | undefined {
@@ -262,6 +264,11 @@ export class ProjectBuildPanel {
await this.handleRemoveConfigFile(message, false, true);
return;
+ // Toggle file between override and extra
+ case "toggleFileExtra":
+ await this.handleToggleFileExtra(message);
+ return;
+
// Build config files
case "addBuildConfigFile":
await addConfigFiles(ctx, ws, true, false, this._selectedProject, message.build);
@@ -279,10 +286,17 @@ export class ProjectBuildPanel {
return;
// Build management
- case "addBuild":
- await addBuildToProject(ws, ctx, message.project);
+ case "addBuild": {
+ const projectName = message.project;
+ const existingBuilds = new Set(Object.keys(ws.projects[projectName]?.buildConfigs ?? {}));
+ await addBuildToProject(ws, ctx, projectName);
+ const newBuild = Object.keys(ws.projects[projectName]?.buildConfigs ?? {}).find(b => !existingBuilds.has(b));
+ if (newBuild) {
+ this._selectedBuildOrTest = `build:${newBuild}`;
+ }
await this.refreshAfterChange();
return;
+ }
case "removeBuild":
await removeBuild(ctx, ws, message.project, message.build);
await this.refreshAfterChange();
@@ -299,10 +313,17 @@ export class ProjectBuildPanel {
return;
// Test management
- case "addTest":
- await addTest(ws, ctx, message.project);
+ case "addTest": {
+ const projectName = message.project;
+ const existingTests = new Set(Object.keys(ws.projects[projectName]?.twisterConfigs ?? {}));
+ await addTest(ws, ctx, projectName);
+ const newTest = Object.keys(ws.projects[projectName]?.twisterConfigs ?? {}).find(t => !existingTests.has(t));
+ if (newTest) {
+ this._selectedBuildOrTest = `test:${newTest}`;
+ }
await this.refreshAfterChange();
return;
+ }
// Runner management
case "addRunner":
@@ -313,6 +334,9 @@ export class ProjectBuildPanel {
await removeRunner(ctx, ws, message.project, message.build, message.runner);
await this.refreshAfterChange();
return;
+ case "updateRunner":
+ await this.handleUpdateRunner(message);
+ return;
// Build actions
case "build":
@@ -374,11 +398,69 @@ export class ProjectBuildPanel {
this._selectedProject,
isPrimary,
[message.file],
- isProject ? undefined : message.build,
+ isProject ? undefined : (message.build || this.getSelectedBuildName()),
);
await this.refreshAfterChange();
}
+ private async handleToggleFileExtra(message: any) {
+ if (!this._selectedProject || !message.file) {
+ return;
+ }
+ const toggleCmd = String(message["toggle-cmd"] ?? "");
+ const toExtra = message.extra === "true";
+ const file = String(message.file);
+
+ // Determine isKConfig, isProject from the toggle command name
+ let isKConfig: boolean;
+ let isProject: boolean;
+ switch (toggleCmd) {
+ case "toggleProjectConfigFileExtra":
+ isKConfig = true; isProject = true; break;
+ case "toggleProjectOverlayFileExtra":
+ isKConfig = false; isProject = true; break;
+ case "toggleBuildConfigFileExtra":
+ isKConfig = true; isProject = false; break;
+ case "toggleBuildOverlayFileExtra":
+ isKConfig = false; isProject = false; break;
+ default:
+ return;
+ }
+
+ const project = this._wsConfig.projects[this._selectedProject];
+ if (!project) { return; }
+
+ let confFiles;
+ if (isProject) {
+ confFiles = project.confFiles;
+ } else {
+ const buildName = this.getSelectedBuildName();
+ if (!buildName || !project.buildConfigs[buildName]) { return; }
+ confFiles = project.buildConfigs[buildName].confFiles;
+ }
+
+ // Remove from current list, add to target list
+ // toExtra=true means moving from primary→extra; isPrimary is the inverse of "is extra"
+ const fromKey = getConfFileKey(isKConfig, toExtra);
+ const toKey = getConfFileKey(isKConfig, !toExtra);
+
+ confFiles[fromKey] = confFiles[fromKey].filter((f: string) => f !== file);
+ if (!confFiles[toKey].includes(file)) {
+ confFiles[toKey].push(file);
+ }
+
+ await setWorkspaceState(this._context, this._wsConfig);
+ this.updateHtml();
+ }
+
+ private getSelectedBuildName(): string | undefined {
+ const sel = this._selectedBuildOrTest;
+ if (sel && sel.startsWith("build:")) {
+ return sel.slice(6);
+ }
+ return undefined;
+ }
+
private async handleUpsertVariable(message: any) {
const key = String(message.key ?? "").trim();
const value = String(message.value ?? "");
@@ -476,6 +558,27 @@ export class ProjectBuildPanel {
this.updateHtml();
}
+ private async handleUpdateRunner(message: any) {
+ const projectName = this._selectedProject;
+ const buildName = String(message.build ?? "");
+ const runnerName = String(message.runner ?? "");
+ if (!projectName || !buildName || !runnerName) {
+ return;
+ }
+ const runner = this._wsConfig.projects[projectName]?.buildConfigs?.[buildName]?.runnerConfigs?.[runnerName];
+ if (!runner) {
+ return;
+ }
+ if (message["runner-type"] !== undefined) {
+ runner.runner = String(message["runner-type"]);
+ }
+ if (message["runner-args"] !== undefined) {
+ runner.args = String(message["runner-args"]);
+ }
+ await setWorkspaceState(this._context, this._wsConfig);
+ await this.refreshAfterChange();
+ }
+
private async refreshAfterChange() {
await vscode.commands.executeCommand("zephyr-ide.update-web-view");
}
@@ -517,16 +620,33 @@ export class ProjectBuildPanel {
// ---------------------------------------------------------------------------
private updateHtml() {
- this._panel.webview.html = this.getHtmlForWebview();
+ const content = this.generateDynamicContent();
+ if (!this._htmlInitialized) {
+ this._panel.webview.html = this.getHtmlShell(content);
+ this._htmlInitialized = true;
+ } else {
+ void this._panel.webview.postMessage({
+ command: "updateContent",
+ ...content,
+ });
+ }
}
- private getHtmlForWebview(): string {
- const nonce = generateNonce();
+ /** Generate only the dynamic parts of the page. */
+ private generateDynamicContent(): {
+ projectOptionsHtml: string;
+ selectorHtml: string;
+ projectHtml: string;
+ buildOrTestHtml: string;
+ calculatedHtml: string;
+ noProjectHtml: string;
+ hasBuildSelected: boolean;
+ } {
const projectNames = Object.keys(this._wsConfig.projects ?? {});
const selected = this._selectedProject;
// Project selector
- const projectOptions = projectNames
+ const projectOptionsHtml = projectNames
.map((name) => {
const sel = name === selected ? " selected" : "";
return `${escapeHtml(name)} `;
@@ -536,6 +656,7 @@ export class ProjectBuildPanel {
// Project section
let projectHtml = "";
let buildOrTestHtml = "";
+ let calculatedHtml = "";
let selectorHtml = "";
if (selected && this._wsConfig.projects[selected]) {
@@ -590,7 +711,16 @@ export class ProjectBuildPanel {
const calculated = getCalculatedConfigFiles(project, project.buildConfigs[buildName]);
const activeBuild = this._wsConfig.projectStates[selected]?.activeBuildConfig;
const isActive = buildName === activeBuild;
- buildOrTestHtml = getBuildSectionHtml(buildDetails, selected, buildName, buildVars, calculated, undefined, isActive);
+ buildOrTestHtml = getBuildSectionHtml(buildDetails, selected, buildName, buildVars, isActive);
+ calculatedHtml = `
+
+
+
+ ${getCalculatedSectionHtml(calculated, undefined)}
+
+
`;
}
} else if (currentSelection.startsWith("test:")) {
const testName = currentSelection.slice(5);
@@ -599,9 +729,29 @@ export class ProjectBuildPanel {
buildOrTestHtml = getTestSectionHtml(testDetails, selected);
}
}
- }
- const variablesRefHtml = getVariablesReferenceSectionHtml();
+ // Show placeholder if no build/test selected
+ if (!buildOrTestHtml && buildNames.length === 0 && testNames.length === 0) {
+ buildOrTestHtml = `
+
+
+
+
No builds configured.
+
+ Create Build
+
+
+
`;
+ } else if (!buildOrTestHtml) {
+ buildOrTestHtml = `
+
+
+
+
Select a build or test to view details.
+
+
`;
+ }
+ }
const noProjectHtml =
projectNames.length === 0
@@ -611,12 +761,31 @@ export class ProjectBuildPanel {
`
: "";
+ const hasBuildSelected = !!(selected && this._selectedBuildOrTest?.startsWith("build:"));
+
+ return {
+ projectOptionsHtml,
+ selectorHtml,
+ projectHtml,
+ buildOrTestHtml,
+ calculatedHtml,
+ noProjectHtml,
+ hasBuildSelected,
+ };
+ }
+
+ private getHtmlShell(content: ReturnType
): string {
+ const nonce = generateNonce();
+ const disabledAttr = content.hasBuildSelected ? "" : " disabled";
+
+ const variablesRefHtml = getVariablesReferenceSectionHtml();
+
return `
-
+
Zephyr IDE: Project Details
${this.getStylesheetLinks()}
@@ -627,19 +796,43 @@ export class ProjectBuildPanel {
Project Details
Inspect configured projects, builds, tests, and derived variables.
-
- Project:
-
- ${projectOptions}
-
+
+
- ${noProjectHtml}
+ ${content.noProjectHtml}
- ${projectHtml}
- ${selectorHtml}
- ${buildOrTestHtml}
+
+
${content.projectHtml}
+
${content.buildOrTestHtml}
+
+
${content.calculatedHtml}
${variablesRefHtml}
@@ -670,7 +863,7 @@ export class ProjectBuildPanel {
);
return `
-
+
`;
}
diff --git a/src/panels/project_build_view/ProjectSection.ts b/src/panels/project_build_view/ProjectSection.ts
index cfd42e1d..7eded77b 100644
--- a/src/panels/project_build_view/ProjectSection.ts
+++ b/src/panels/project_build_view/ProjectSection.ts
@@ -17,51 +17,7 @@ limitations under the License.
import { ProjectInfo } from "../../project_utilities/project_info";
import { escapeHtml } from "../webview_shared/webviewTypes";
-
-function fileListHtml(files: string[], groupId: string, isExtra: boolean, removeCmd: string): string {
- if (files.length === 0) {
- return `None
`;
- }
- return files
- .map((f) => {
- const escaped = escapeHtml(f);
- const extraFlag = isExtra ? "true" : "false";
- return `
- ${escaped}
-
-
-
-
`;
- })
- .join("\n");
-}
-
-function fileGroupHtml(
- title: string,
- groupId: string,
- primaryFiles: string[],
- extraFiles: string[],
- addCmd: string,
- removeCmd: string,
-): string {
- return `
-
-
-
-
Override Files
- ${fileListHtml(primaryFiles, groupId, false, removeCmd)}
-
-
-
Extra Files
- ${fileListHtml(extraFiles, groupId, true, removeCmd)}
-
-
`;
-}
+import { tabbedConfigGroupHtml } from "./configFileGroup";
function variablesTableHtml(
vars: Record,
@@ -82,11 +38,9 @@ function variablesTableHtml(
-
-
+
-
-
+
`,
)
@@ -94,8 +48,7 @@ function variablesTableHtml(
-
-
+
@@ -134,33 +87,27 @@ export function getProjectSectionHtml(
${cmakeFile}
- ${fileGroupHtml(
- "KConfig Files",
- "kconfig-project",
+ ${tabbedConfigGroupHtml(
+ "project",
projectInfo.confFiles.config,
projectInfo.confFiles.extraConfig,
"addProjectConfigFile",
"removeProjectConfigFile",
- )}
-
- ${fileGroupHtml(
- "DTC Overlay Files",
- "overlay-project",
+ "toggleProjectConfigFileExtra",
projectInfo.confFiles.overlay,
projectInfo.confFiles.extraOverlay,
"addProjectOverlayFile",
"removeProjectOverlayFile",
+ "toggleProjectOverlayFileExtra",
)}
${variablesTableHtml(projectVars, "project", projectName)}
-
-
+
Add Build
-
-
+
Add Test
diff --git a/src/panels/project_build_view/configFileGroup.ts b/src/panels/project_build_view/configFileGroup.ts
new file mode 100644
index 00000000..c0758acf
--- /dev/null
+++ b/src/panels/project_build_view/configFileGroup.ts
@@ -0,0 +1,118 @@
+/*
+Copyright 2024 mylonics
+Author Rijesh Augustine
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { escapeHtml } from "../webview_shared/webviewTypes";
+
+/**
+ * Renders a single file row with an "Extra" checkbox, clickable filename, and remove button.
+ */
+function fileRowHtml(
+ file: string,
+ groupId: string,
+ isExtra: boolean,
+ removeCmd: string,
+ toggleCmd: string,
+): string {
+ const escaped = escapeHtml(file);
+ const extraFlag = isExtra ? "true" : "false";
+ const toggleToExtraFlag = isExtra ? "false" : "true";
+ const modeLabel = isExtra ? "Extra" : "Override";
+ return `
+
+ ${modeLabel}
+
+ ${escaped}
+
+
+
`;
+}
+
+/**
+ * Renders a scrollable file list for one tab (KConfig or DTC).
+ */
+function fileTabContentHtml(
+ groupId: string,
+ primaryFiles: string[],
+ extraFiles: string[],
+ addCmd: string,
+ removeCmd: string,
+ toggleCmd: string,
+ addLabel: string,
+): string {
+ const allRows: string[] = [];
+
+ for (const f of primaryFiles) {
+ allRows.push(fileRowHtml(f, groupId, false, removeCmd, toggleCmd));
+ }
+ for (const f of extraFiles) {
+ allRows.push(fileRowHtml(f, groupId, true, removeCmd, toggleCmd));
+ }
+
+ const emptyNotice = allRows.length === 0
+ ? '
No files configured
'
+ : "";
+
+ return `
+
+
+
+ ${allRows.join("\n")}
+ ${emptyNotice}
+
+
`;
+}
+
+/**
+ * Renders a tabbed config file group with Kconfig and Devicetree Overlay tabs.
+ * Each tab shows a unified list of files with an Extra checkbox column.
+ */
+export function tabbedConfigGroupHtml(
+ idPrefix: string,
+ kconfigPrimary: string[],
+ kconfigExtra: string[],
+ kconfigAddCmd: string,
+ kconfigRemoveCmd: string,
+ kconfigToggleCmd: string,
+ overlayPrimary: string[],
+ overlayExtra: string[],
+ overlayAddCmd: string,
+ overlayRemoveCmd: string,
+ overlayToggleCmd: string,
+): string {
+ const kconfigGroupId = `kconfig-${idPrefix}`;
+ const overlayGroupId = `overlay-${idPrefix}`;
+
+ return `
+
+
+ Kconfig Files
+
+ ${fileTabContentHtml(kconfigGroupId, kconfigPrimary, kconfigExtra, kconfigAddCmd, kconfigRemoveCmd, kconfigToggleCmd, "Add Kconfig")}
+
+ Devicetree Overlay Files
+
+ ${fileTabContentHtml(overlayGroupId, overlayPrimary, overlayExtra, overlayAddCmd, overlayRemoveCmd, overlayToggleCmd, "Add Overlay")}
+
+
+
`;
+}
diff --git a/src/panels/project_build_view/project-build-panel.css b/src/panels/project_build_view/project-build-panel.css
index 6c82e28c..4ca83280 100644
--- a/src/panels/project_build_view/project-build-panel.css
+++ b/src/panels/project_build_view/project-build-panel.css
@@ -29,12 +29,30 @@
min-width: 180px;
}
+/* Page header selectors row */
+.page-header-selectors {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ flex-wrap: wrap;
+}
+
+/* Page header build action buttons */
+.page-header-actions {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 16px;
+ padding-bottom: 14px;
+ border-bottom: 1px solid var(--vscode-panel-border);
+}
+
/* Build / test selector toolbar */
.build-test-selector {
display: flex;
align-items: center;
gap: 8px;
- padding: 8px 0;
}
.build-test-selector label {
@@ -49,7 +67,73 @@
/* Test section */
.test-section {
- border-left: 3px solid var(--vscode-panel-border);
+ border-top: 3px solid var(--vscode-panel-border);
+}
+
+/* Side-by-side project + build layout */
+.project-build-row {
+ display: flex;
+ gap: 12px;
+ align-items: stretch;
+ height: clamp(480px, 65vh, 860px);
+}
+
+.project-build-col {
+ flex: 1;
+ min-width: 0;
+ min-height: 0;
+ display: flex;
+}
+
+.project-build-col > .panel-section {
+ flex: 1;
+ min-height: 0;
+ margin-bottom: 0;
+ display: flex;
+ flex-direction: column;
+}
+
+.project-build-col > .panel-section > .section-body {
+ flex: 1;
+ min-height: 0;
+ overflow: auto;
+}
+
+@media (max-width: 900px) {
+ .project-build-row {
+ flex-direction: column;
+ height: auto;
+ }
+
+ .project-build-col > .panel-section {
+ min-height: auto;
+ }
+}
+
+/* Build placeholder (when no build selected) */
+.build-placeholder {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 200px;
+}
+
+.build-placeholder-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ color: var(--vscode-descriptionForeground);
+ text-align: center;
+ padding: 32px 16px;
+}
+
+.build-placeholder-content i {
+ font-size: 2em;
+}
+
+.build-placeholder-content p {
+ margin: 0;
}
/* Sections */
@@ -138,7 +222,7 @@
border-top: 1px solid var(--vscode-panel-border);
}
-/* Config file groups */
+/* Config file groups (tabbed) */
.config-group {
margin: 12px 0;
border: 1px solid var(--vscode-panel-border);
@@ -147,40 +231,61 @@
box-shadow: var(--zephyr-webview-surface-shadow);
}
-.config-group-header {
+.config-tab-body {
+ padding: 0;
+}
+
+.config-tab-header-row {
display: flex;
align-items: center;
- justify-content: space-between;
- padding: 6px 12px;
+ padding: 4px 12px;
+ gap: 8px;
+ border-bottom: 1px solid var(--vscode-panel-border);
background-color: var(--vscode-sideBar-background);
+ font-size: 0.85em;
font-weight: 500;
+ color: var(--vscode-descriptionForeground);
}
-.config-group-title {
- display: flex;
- align-items: center;
- gap: 6px;
+.config-tab-col-extra {
+ flex: 0 0 90px;
+ text-align: center;
}
-.config-group-sub {
- padding: 4px 12px 8px;
+.config-tab-col-file {
+ flex: 1;
}
-.config-sub-label {
- font-size: 0.9em;
- color: var(--vscode-descriptionForeground);
- margin: 6px 0 2px;
- font-weight: 500;
+.config-tab-add-button {
+ margin-left: auto;
+}
+
+.config-file-scroll {
+ max-height: 200px;
}
.file-list-item {
display: flex;
align-items: center;
- justify-content: space-between;
- padding: 2px 4px;
+ padding: 2px 12px;
+ gap: 8px;
border-radius: var(--zephyr-webview-radius);
}
+.file-list-item .file-mode-button {
+ flex: 0 0 90px;
+ display: flex;
+ justify-content: center;
+}
+
+.file-mode-button {
+ flex: 0 0 90px;
+}
+
+.file-remove-button {
+ margin-left: auto;
+}
+
.file-list-item:hover {
background-color: var(--vscode-list-hoverBackground);
}
@@ -207,12 +312,10 @@
}
/* Build section */
-.build-section {
- border-left: 3px solid var(--vscode-panel-border);
-}
-
-.build-section.build-active {
- border-left-color: var(--vscode-charts-green, #89d185);
+.build-section .section-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
}
.build-header {
@@ -221,6 +324,23 @@
gap: 8px;
}
+.build-details-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+ gap: 12px;
+ align-items: start;
+}
+
+.build-details-col {
+ min-width: 0;
+}
+
+@media (max-width: 1200px) {
+ .build-details-grid {
+ grid-template-columns: minmax(0, 1fr);
+ }
+}
+
/* Section row headers (variables, runners, launch configs) */
.section-row-header {
display: flex;
@@ -405,7 +525,7 @@
.runner-row {
display: flex;
- align-items: center;
+ align-items: flex-start;
padding: 6px 8px;
gap: 12px;
border: 1px solid var(--vscode-panel-border);
@@ -433,6 +553,49 @@
align-items: center;
gap: 4px;
min-width: 100px;
+ padding-top: 4px;
+}
+
+.runner-fields {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.runner-field-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.runner-field-label {
+ font-size: 0.85em;
+ color: var(--vscode-descriptionForeground);
+ min-width: 50px;
+}
+
+.runner-input {
+ flex: 1;
+ background: var(--vscode-input-background);
+ color: var(--vscode-input-foreground);
+ border: 1px solid var(--vscode-input-border, transparent);
+ padding: 3px 6px;
+ font-family: var(--vscode-editor-font-family);
+ font-size: var(--vscode-editor-font-size);
+ border-radius: 2px;
+}
+
+.runner-input:focus {
+ outline: 1px solid var(--vscode-focusBorder);
+ outline-offset: -1px;
+}
+
+.runner-actions {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ padding-top: 2px;
}
.runner-detail {
diff --git a/src/panels/project_build_view/project-build-panel.ts b/src/panels/project_build_view/project-build-panel.ts
index 1a1589b4..a0756579 100644
--- a/src/panels/project_build_view/project-build-panel.ts
+++ b/src/panels/project_build_view/project-build-panel.ts
@@ -29,6 +29,10 @@ import '@vscode-elements/elements/dist/vscode-table-body/index.js';
import '@vscode-elements/elements/dist/vscode-table-row/index.js';
import '@vscode-elements/elements/dist/vscode-table-cell/index.js';
import '@vscode-elements/elements/dist/vscode-button-group/index.js';
+import '@vscode-elements/elements/dist/vscode-tabs/index.js';
+import '@vscode-elements/elements/dist/vscode-tab-header/index.js';
+import '@vscode-elements/elements/dist/vscode-tab-panel/index.js';
+import '@vscode-elements/elements/dist/vscode-scrollable/index.js';
import { getVsCodeApi } from "../webview_shared/webviewTypes";
@@ -39,39 +43,24 @@ function sendCommand(command: string, data: Record
= {}): void {
}
// ---------------------------------------------------------------------------
-// Project selector
+// Delegated change handlers (vscode-elements fire 'vsc-change')
// ---------------------------------------------------------------------------
-function setupProjectSelector(): void {
- const select = document.getElementById("projectSelect") as HTMLElement | null;
- if (select) {
- select.addEventListener("vsc-change", (e: Event) => {
- const value = (e.target as any).value;
- sendCommand("switchProject", { project: value });
- });
- }
-}
-
-// ---------------------------------------------------------------------------
-// Build / test selector
-// ---------------------------------------------------------------------------
-
-function setupBuildTestSelector(): void {
- const select = document.getElementById("buildTestSelect") as HTMLElement | null;
- if (select) {
- select.addEventListener("vsc-change", (e: Event) => {
- const value = (e.target as any).value;
- sendCommand("switchBuildOrTest", { selection: value });
- });
- }
-}
-
-// ---------------------------------------------------------------------------
-// Collapsible sections
-// ---------------------------------------------------------------------------
+function setupChangeHandlers(): void {
+ // vscode-single-select fires non-bubbling 'change' events on the host element.
+ // Use capture phase to intercept them at document level.
+ document.addEventListener("change", (e: Event) => {
+ const target = e.target;
+ if (!(target instanceof HTMLElement)) { return; }
+
+ const id = target.id;
+ if (id === "projectSelect") {
+ sendCommand("switchProject", { project: (target as any).value });
+ } else if (id === "buildTestSelect") {
+ sendCommand("switchBuildOrTest", { selection: (target as any).value });
+ }
+ }, true); // capture phase
-function setupCollapsibles(): void {
- // vscode-collapsible handles its own open/close natively; no manual toggle needed.
}
// ---------------------------------------------------------------------------
@@ -131,6 +120,12 @@ function setupClickDelegation(): void {
const row = el.closest(".build-arg-row");
const argInput = row?.querySelector(".build-arg-input") as HTMLInputElement | null;
data.value = argInput?.value ?? "";
+ } else if (command === "updateRunner") {
+ const row = el.closest(".runner-row");
+ const runnerInput = row?.querySelector(".runner-runner-input") as HTMLInputElement | null;
+ const argsInput = row?.querySelector(".runner-args-input") as HTMLInputElement | null;
+ data["runner-type"] = runnerInput?.value ?? "";
+ data["runner-args"] = argsInput?.value ?? "";
}
sendCommand(command, data);
@@ -147,14 +142,22 @@ function setupClickDelegation(): void {
button.click();
return;
}
- if (!hasBuildArgInputClass(target)) {
+ if (hasBuildArgInputClass(target)) {
+ const row = target?.closest(".build-arg-row");
+ const button = row?.querySelector('[data-command="upsertBuildArg"]') as HTMLElement | null;
+ if (!button) { return; }
+ e.preventDefault();
+ button.click();
+ return;
+ }
+ if (target?.classList.contains("runner-input")) {
+ const row = target?.closest(".runner-row");
+ const button = row?.querySelector('[data-command="updateRunner"]') as HTMLElement | null;
+ if (!button) { return; }
+ e.preventDefault();
+ button.click();
return;
}
- const row = target?.closest(".build-arg-row");
- const button = row?.querySelector('[data-command="upsertBuildArg"]') as HTMLElement | null;
- if (!button) { return; }
- e.preventDefault();
- button.click();
}, listenerOptions);
const handleKeyboardCommand = (e: KeyboardEvent): void => {
@@ -178,21 +181,128 @@ function setupClickDelegation(): void {
window.addEventListener("unload", () => eventController.abort(), { once: true });
}
+// ---------------------------------------------------------------------------
+// Tab state preservation
+// ---------------------------------------------------------------------------
+
+type TabState = Record;
+
+function captureTabState(): TabState {
+ const state: TabState = {};
+ document.querySelectorAll("vscode-tabs[data-tab-id]").forEach((tabs) => {
+ const id = tabs.getAttribute("data-tab-id");
+ if (id) {
+ state[id] = (tabs as any).selectedIndex ?? 0;
+ }
+ });
+ return state;
+}
+
+function restoreTabState(state: TabState): void {
+ document.querySelectorAll("vscode-tabs[data-tab-id]").forEach((tabs) => {
+ const id = tabs.getAttribute("data-tab-id");
+ if (id && state[id] !== undefined) {
+ (tabs as any).selectedIndex = state[id];
+ }
+ });
+}
+
+function saveTabStateToPersistent(): void {
+ const tabState = captureTabState();
+ const existing = vscode.getState() ?? {};
+ vscode.setState({ ...existing, tabState });
+}
+
+function restoreTabStateFromPersistent(): void {
+ const saved = vscode.getState() as Record | undefined;
+ if (saved?.tabState) {
+ restoreTabState(saved.tabState as TabState);
+ }
+}
+
+// ---------------------------------------------------------------------------
+// DOM content update (in-place, preserving tab state)
+// ---------------------------------------------------------------------------
+
+function applyContentUpdate(message: any): void {
+ const tabState = captureTabState();
+
+ const projectCol = document.getElementById("projectCol");
+ const buildTestCol = document.getElementById("buildTestCol");
+ const calculatedArea = document.getElementById("calculatedArea");
+ const noProjectArea = document.getElementById("noProjectArea");
+ const selectorContainer = document.getElementById("buildTestSelectorContainer");
+ const headerActions = document.getElementById("headerActions");
+
+ if (projectCol && message.projectHtml !== undefined) {
+ projectCol.innerHTML = message.projectHtml;
+ }
+ if (buildTestCol && message.buildOrTestHtml !== undefined) {
+ buildTestCol.innerHTML = message.buildOrTestHtml;
+ }
+ if (calculatedArea && message.calculatedHtml !== undefined) {
+ calculatedArea.innerHTML = message.calculatedHtml;
+ }
+ if (noProjectArea && message.noProjectHtml !== undefined) {
+ noProjectArea.innerHTML = message.noProjectHtml;
+ }
+ if (selectorContainer && message.selectorHtml !== undefined) {
+ selectorContainer.innerHTML = message.selectorHtml;
+ }
+
+ // Update header action button disabled states
+ if (headerActions) {
+ const buttons = headerActions.querySelectorAll("vscode-button");
+ buttons.forEach((btn) => {
+ if (message.hasBuildSelected) {
+ btn.removeAttribute("disabled");
+ } else {
+ btn.setAttribute("disabled", "");
+ }
+ });
+ }
+
+ // Update project selector options
+ const projectSelect = document.getElementById("projectSelect");
+ if (projectSelect && message.projectOptionsHtml !== undefined) {
+ (projectSelect as HTMLElement).innerHTML = message.projectOptionsHtml;
+ }
+
+ // Restore tab state after a microtask to let custom elements render
+ requestAnimationFrame(() => {
+ restoreTabState(tabState);
+ saveTabStateToPersistent();
+ });
+}
+
// ---------------------------------------------------------------------------
// Initialize
// ---------------------------------------------------------------------------
function init(): void {
- setupProjectSelector();
- setupBuildTestSelector();
- setupCollapsibles();
+ setupChangeHandlers();
setupClickDelegation();
+
+ // Monitor tab changes to persist state
+ document.addEventListener("click", (e) => {
+ const target = e.target as HTMLElement | null;
+ if (target?.tagName?.toLowerCase() === "vscode-tab-header") {
+ // Delay slightly so the tab selection updates first
+ requestAnimationFrame(() => saveTabStateToPersistent());
+ }
+ });
+
+ // Restore tabs from persisted state on initial load
+ requestAnimationFrame(() => restoreTabStateFromPersistent());
}
// Handle messages from extension
window.addEventListener("message", (event) => {
const message = event.data;
switch (message.command) {
+ case "updateContent":
+ applyContentUpdate(message);
+ break;
case "refresh":
// Full content replacement handled by extension re-setting HTML
break;
diff --git a/src/panels/sdk_panel/SDKPanel.ts b/src/panels/sdk_panel/SDKPanel.ts
new file mode 100644
index 00000000..3746f58a
--- /dev/null
+++ b/src/panels/sdk_panel/SDKPanel.ts
@@ -0,0 +1,329 @@
+/*
+Copyright 2024 mylonics
+Author Rijesh Augustine
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import * as vscode from "vscode";
+import { WorkspaceConfig, GlobalConfig } from "../../setup_utilities/types";
+import {
+ getWestSDKContext,
+ listAvailableSDKs,
+ ParsedSDKList,
+ onSDKProgress,
+} from "../../setup_utilities/west_sdk";
+import { notifyError, outputError } from "../../utilities/output";
+
+export class SDKPanel {
+ public static currentPanel: SDKPanel | undefined;
+ private readonly _panel: vscode.WebviewPanel;
+ private readonly _extensionPath: string;
+ private readonly _context: vscode.ExtensionContext;
+ private _disposables: vscode.Disposable[] = [];
+
+ private currentWsConfig?: WorkspaceConfig;
+ private currentGlobalConfig?: GlobalConfig;
+
+ /** Cached SDK list fetched in the background. */
+ private _cachedSDKList?: ParsedSDKList;
+ /** True while a background SDK list fetch is in flight. */
+ private _sdkListFetching = false;
+
+ /** Update all open SDK panels with new config */
+ public static updateAllPanels(wsConfig: WorkspaceConfig, globalConfig: GlobalConfig) {
+ if (SDKPanel.currentPanel) {
+ SDKPanel.currentPanel.updateContent(wsConfig, globalConfig);
+ }
+ }
+
+ public static createOrShow(
+ extensionPath: string,
+ context: vscode.ExtensionContext,
+ wsConfig: WorkspaceConfig,
+ globalConfig: GlobalConfig,
+ ) {
+ const column = vscode.window.activeTextEditor
+ ? vscode.window.activeTextEditor.viewColumn
+ : undefined;
+
+ if (SDKPanel.currentPanel) {
+ SDKPanel.currentPanel._panel.reveal(column);
+ SDKPanel.currentPanel.updateContent(wsConfig, globalConfig);
+ return SDKPanel.currentPanel;
+ }
+
+ const panel = vscode.window.createWebviewPanel(
+ "zephyrIDESDK",
+ "Zephyr IDE: SDK",
+ column || vscode.ViewColumn.One,
+ {
+ enableScripts: true,
+ retainContextWhenHidden: true,
+ localResourceRoots: [vscode.Uri.file(extensionPath)],
+ },
+ );
+
+ SDKPanel.currentPanel = new SDKPanel(
+ panel,
+ extensionPath,
+ context,
+ wsConfig,
+ globalConfig,
+ );
+ return SDKPanel.currentPanel;
+ }
+
+ private constructor(
+ panel: vscode.WebviewPanel,
+ extensionPath: string,
+ context: vscode.ExtensionContext,
+ wsConfig: WorkspaceConfig,
+ globalConfig: GlobalConfig,
+ ) {
+ this._panel = panel;
+ this._extensionPath = extensionPath;
+ this._context = context;
+
+ this.updateContent(wsConfig, globalConfig);
+
+ this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
+
+ this._panel.webview.onDidReceiveMessage(
+ (message) => { this.handleWebviewMessage(message); },
+ null,
+ this._disposables,
+ );
+
+ // Subscribe to SDK install progress events and forward to webview
+ this._disposables.push(
+ onSDKProgress((event) => {
+ this._panel.webview.postMessage({
+ command: 'sdkInstallProgress',
+ data: event,
+ });
+ }),
+ );
+
+ // Pre-fetch SDK list in the background
+ void this.fetchSDKListInBackground();
+ }
+
+ public updateContent(wsConfig: WorkspaceConfig, globalConfig: GlobalConfig) {
+ this.currentWsConfig = wsConfig;
+ this.currentGlobalConfig = globalConfig;
+ this._panel.webview.html = this.getHtmlForWebview(wsConfig, globalConfig);
+
+ // Push cached SDK list immediately if available, otherwise start fetching
+ if (this._cachedSDKList) {
+ this._panel.webview.postMessage({
+ command: "sdkListResult",
+ data: this._cachedSDKList,
+ });
+ } else if (this.hasValidSetupState()) {
+ this._panel.webview.postMessage({ command: "sdkListLoading" });
+ void this.fetchSDKListInBackground();
+ }
+ }
+
+ public dispose() {
+ SDKPanel.currentPanel = undefined;
+ this._panel.dispose();
+ while (this._disposables.length) {
+ const x = this._disposables.pop();
+ if (x) { x.dispose(); }
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ // Helpers
+ // ---------------------------------------------------------------------------
+
+ private hasValidSetupState(): boolean {
+ return this.currentGlobalConfig?.setupStateDictionary !== undefined &&
+ Object.keys(this.currentGlobalConfig.setupStateDictionary).length > 0;
+ }
+
+ private handleWebviewMessage(message: any) {
+ switch (message.command) {
+ case "installSDK":
+ this.installSDK();
+ return;
+ case "listSDKs":
+ this.listSDKs();
+ return;
+ case "openSetupPanel":
+ vscode.commands.executeCommand("zephyr-ide.open-setup-panel");
+ return;
+ }
+ }
+
+ private async installSDK() {
+ try {
+ await vscode.commands.executeCommand("zephyr-ide.install-sdk");
+ if (this.currentWsConfig && this.currentGlobalConfig) {
+ try {
+ this._cachedSDKList = undefined;
+ this.updateContent(this.currentWsConfig, this.currentGlobalConfig);
+ await this.listSDKs();
+ } catch (updateError) {
+ outputError("SDK Panel", `Failed to refresh panel after SDK installation: ${String(updateError)}`);
+ }
+ }
+ } catch (error) {
+ notifyError("SDK Install", `Failed to install west SDK: ${error}`);
+ }
+ }
+
+ private async fetchSDKListInBackground() {
+ if (this._sdkListFetching || !this.hasValidSetupState()) {
+ return;
+ }
+ if (!this.currentWsConfig || !this.currentGlobalConfig) {
+ return;
+ }
+
+ this._sdkListFetching = true;
+ try {
+ const setupState = await getWestSDKContext(
+ this.currentWsConfig,
+ this.currentGlobalConfig,
+ this._context,
+ );
+ if (!setupState) { return; }
+ const sdkList = await listAvailableSDKs(setupState);
+ this._cachedSDKList = sdkList;
+
+ // Push to webview if still open
+ this._panel.webview.postMessage({
+ command: "sdkListResult",
+ data: sdkList,
+ });
+ } catch {
+ // Silently ignore background fetch failures
+ } finally {
+ this._sdkListFetching = false;
+ }
+ }
+
+ private async listSDKs() {
+ try {
+ if (!this.currentWsConfig || !this.currentGlobalConfig) {
+ notifyError("SDK List", "Configuration not available");
+ return;
+ }
+
+ const setupState = await getWestSDKContext(
+ this.currentWsConfig,
+ this.currentGlobalConfig,
+ this._context,
+ );
+ if (!setupState) {
+ notifyError("SDK List", "No valid west installation found for SDK management");
+ return;
+ }
+
+ const sdkList = await listAvailableSDKs(setupState);
+ this._cachedSDKList = sdkList;
+
+ this._panel.webview.postMessage({
+ command: "sdkListResult",
+ data: sdkList,
+ });
+ } catch (error) {
+ notifyError("SDK List", `Failed to list SDKs: ${error}`);
+ this._panel.webview.postMessage({
+ command: "sdkListResult",
+ data: {
+ success: false,
+ versions: [],
+ error: `Failed to list SDKs: ${error}`,
+ },
+ });
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ // HTML Generation
+ // ---------------------------------------------------------------------------
+
+ private getHtmlForWebview(wsConfig: WorkspaceConfig, globalConfig: GlobalConfig): string {
+ const hasSetupState = this.hasValidSetupState();
+ const statusIcon = globalConfig.sdkInstalled ? '✓' : hasSetupState ? '⚙' : '⚠';
+ const statusLabel = globalConfig.sdkInstalled ? 'Installed' : hasSetupState ? 'Not Installed' : 'Workspace Required';
+ const statusClass = globalConfig.sdkInstalled ? 'status-success' : hasSetupState ? 'status-warning' : 'status-error';
+
+ const warningSection = !hasSetupState ? `
+
+
+ No West Workspace Found
+ A west workspace must be set up before SDK toolchains can be installed or managed.
+ Set up a workspace first using the Setup panel.
+
+
` : '';
+
+ const cssUri = this._panel.webview.asWebviewUri(
+ vscode.Uri.joinPath(vscode.Uri.file(this._extensionPath), "src", "panels", "sdk_panel", "sdk-panel.css"),
+ );
+ const codiconUri = this._panel.webview.asWebviewUri(
+ vscode.Uri.joinPath(vscode.Uri.file(this._extensionPath), "node_modules", "@vscode", "codicons", "dist", "codicon.css"),
+ );
+ const jsUri = this._panel.webview.asWebviewUri(
+ vscode.Uri.joinPath(vscode.Uri.file(this._extensionPath), "dist", "webview", "sdk_panel", "sdk-panel.js"),
+ );
+
+ return `
+
+
+
+
+ Zephyr SDK
+
+
+
+
+
+
+
+
+
The Zephyr SDK provides GNU toolchains for cross-compiling to supported target architectures. Install or update toolchains below, then refresh to see what's available.
+
+ ${warningSection}
+
+
+
+
+
+
+ `;
+ }
+}
diff --git a/src/panels/sdk_panel/sdk-panel.css b/src/panels/sdk_panel/sdk-panel.css
new file mode 100644
index 00000000..2b42fb51
--- /dev/null
+++ b/src/panels/sdk_panel/sdk-panel.css
@@ -0,0 +1,124 @@
+/*
+ * SDK Panel styles.
+ * Base styles (status, buttons, info-box, etc.) are inherited from webview-base.css.
+ */
+@import url("../webview_shared/webview-base.css");
+
+/* ===================================================================
+ SDK list
+ =================================================================== */
+
+.sdk-list-container {
+ margin-top: 20px;
+}
+
+.sdk-version-card {
+ border: 1px solid var(--vscode-panel-border);
+ border-radius: var(--zephyr-webview-radius);
+ padding: 15px;
+ margin-bottom: 12px;
+ background-color: var(--vscode-editor-background);
+}
+
+.sdk-version-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+.sdk-version-title {
+ font-weight: 600;
+ font-size: 14px;
+}
+
+.sdk-path {
+ font-family: var(--vscode-editor-font-family);
+ font-size: 11px;
+ color: var(--vscode-descriptionForeground);
+ background-color: var(--vscode-textCodeBlock-background);
+ padding: 4px 8px;
+ border-radius: 4px;
+ margin-bottom: 12px;
+ word-break: break-all;
+}
+
+.toolchain-section {
+ margin-top: 12px;
+}
+
+.toolchain-section-title {
+ font-size: 12px;
+ font-weight: 600;
+ margin-bottom: 6px;
+ color: var(--vscode-foreground);
+}
+
+.toolchain-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+}
+
+.toolchain-tag {
+ background-color: var(--vscode-badge-background);
+ color: var(--vscode-badge-foreground);
+ padding: 2px 6px;
+ border-radius: 4px;
+ font-size: 11px;
+ font-family: var(--vscode-editor-font-family);
+}
+
+.toolchain-tag.available {
+ background-color: var(--vscode-button-secondaryBackground);
+ color: var(--vscode-button-secondaryForeground);
+ opacity: 0.7;
+}
+
+.sdk-loading {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 10px;
+}
+
+.sdk-error-box {
+ padding: 15px;
+ border: 1px solid var(--vscode-inputValidation-errorBorder);
+ border-radius: var(--zephyr-webview-radius);
+ background-color: var(--vscode-inputValidation-errorBackground);
+ color: var(--vscode-inputValidation-errorForeground);
+}
+
+.error-box {
+ padding: 15px;
+ border: 1px solid var(--vscode-inputValidation-errorBorder);
+ border-radius: var(--zephyr-webview-radius);
+ background-color: var(--vscode-inputValidation-errorBackground);
+ color: var(--vscode-inputValidation-errorForeground);
+ margin-bottom: 16px;
+}
+
+.sdk-description {
+ margin: 0 0 16px 0;
+ font-size: 13px;
+ line-height: 1.5;
+ color: var(--vscode-descriptionForeground);
+}
+
+.sdk-empty-box {
+ padding: 15px;
+ border: 1px solid var(--vscode-panel-border);
+ border-radius: var(--zephyr-webview-radius);
+ background-color: var(--vscode-editor-background);
+ color: var(--vscode-descriptionForeground);
+ text-align: center;
+}
+
+/* ===================================================================
+ Header status badge — styles inherited from webview-base.css
+ =================================================================== */
+
+/* ===================================================================
+ SDK install progress — styles inherited from webview-base.css
+ =================================================================== */
diff --git a/src/panels/sdk_panel/sdk-panel.ts b/src/panels/sdk_panel/sdk-panel.ts
new file mode 100644
index 00000000..dd09e755
--- /dev/null
+++ b/src/panels/sdk_panel/sdk-panel.ts
@@ -0,0 +1,274 @@
+/*
+Copyright 2024 mylonics
+Author Rijesh Augustine
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// SDK Panel Client-Side Logic
+
+import '@vscode-elements/elements/dist/vscode-button/index.js';
+import '@vscode-elements/elements/dist/vscode-icon/index.js';
+import '@vscode-elements/elements/dist/vscode-progress-ring/index.js';
+import { getVsCodeApi, escapeHtml } from '../webview_shared/webviewTypes';
+
+const vscode = getVsCodeApi();
+
+// ---------------------------------------------------------------------------
+// SDK Management
+// ---------------------------------------------------------------------------
+
+function listSDKs(): void {
+ const containerDiv = document.getElementById('sdkListContainer');
+ if (containerDiv) {
+ containerDiv.innerHTML = 'Loading SDK information...
';
+ }
+ setSDKButtonsDisabled(true);
+ vscode.postMessage({ command: 'listSDKs' });
+}
+
+function installSDK(): void {
+ setSDKButtonsDisabled(true);
+ vscode.postMessage({ command: 'installSDK' });
+}
+
+function setSDKButtonsDisabled(disabled: boolean): void {
+ const installBtn = document.getElementById('sdkInstallBtn');
+ const listBtn = document.getElementById('sdkListBtn');
+ if (installBtn) { (installBtn as any).disabled = disabled; }
+ if (listBtn) { (listBtn as any).disabled = disabled; }
+}
+
+function showSDKListLoading(): void {
+ const containerDiv = document.getElementById('sdkListContainer');
+ if (containerDiv) {
+ containerDiv.innerHTML = 'Loading SDK information...
';
+ }
+}
+
+// ---------------------------------------------------------------------------
+// SDK list display
+// ---------------------------------------------------------------------------
+
+interface SDKVersion {
+ version?: string;
+ path?: string;
+ installedToolchains?: string[];
+ availableToolchains?: string[];
+}
+
+interface SDKListData {
+ success: boolean;
+ versions?: SDKVersion[];
+ error?: string;
+}
+
+function displaySDKList(sdkData: SDKListData): void {
+ const containerDiv = document.getElementById('sdkListContainer');
+ if (!containerDiv) { return; }
+
+ setSDKButtonsDisabled(false);
+
+ if (!sdkData.success) {
+ containerDiv.innerHTML = `
+
+
Error: ${escapeHtml(sdkData.error || 'Failed to list SDKs')}
+
+
+
+ Retry
+
+
+
`;
+ return;
+ }
+
+ if (!sdkData.versions || sdkData.versions.length === 0) {
+ containerDiv.innerHTML = `
+
+ No SDK versions found. Try installing an SDK first.
+
`;
+ return;
+ }
+
+ let html = '';
+ for (const version of sdkData.versions) {
+ const escapedVersion = escapeHtml(version.version || '');
+ const escapedPath = escapeHtml(version.path || '');
+
+ html += `
+
+
+
${escapedPath}
+ ${version.installedToolchains && version.installedToolchains.length > 0 ? `
+
` : ''}
+ ${version.availableToolchains && version.availableToolchains.length > 0 ? `
+
` : ''}
+
`;
+ }
+ containerDiv.innerHTML = html;
+}
+
+// ---------------------------------------------------------------------------
+// SDK install progress
+// ---------------------------------------------------------------------------
+
+interface SetupProgressStep {
+ id: string;
+ label: string;
+ status: string;
+ detail?: string;
+}
+
+interface SetupProgressData {
+ type: 'start' | 'step-update' | 'complete' | 'failed';
+ operationLabel: string;
+ steps: SetupProgressStep[];
+ message?: string;
+}
+
+function handleSDKInstallProgress(data: SetupProgressData): void {
+ const container = document.getElementById('sdkProgressContainer');
+ if (!container) { return; }
+
+ if (data.type === 'complete' || data.type === 'failed') {
+ setSDKButtonsDisabled(false);
+ }
+
+ let bannerClass: string, bannerIcon: string, bannerText: string;
+ switch (data.type) {
+ case 'complete':
+ bannerClass = 'status-success';
+ bannerIcon = ' ';
+ bannerText = 'Installation Complete';
+ break;
+ case 'failed':
+ bannerClass = 'status-error';
+ bannerIcon = ' ';
+ bannerText = 'Installation Failed';
+ break;
+ default:
+ bannerClass = 'status-info';
+ bannerIcon = '';
+ bannerText = 'Installing SDK...';
+ break;
+ }
+
+ function getStepIcon(status: string): string {
+ switch (status) {
+ case 'completed':
+ return ' ';
+ case 'in-progress':
+ return ' ';
+ case 'failed':
+ return ' ';
+ case 'skipped':
+ return ' ';
+ default:
+ return ' ';
+ }
+ }
+
+ const stepsHtml = data.steps.map(step => {
+ const detail = step.detail ? `${escapeHtml(step.detail)} ` : '';
+ return `
+ ${getStepIcon(step.status)}
+
+ ${escapeHtml(step.label)}
+ ${detail}
+
+
`;
+ }).join('');
+
+ const bannerSpinner = (data.type === 'start' || data.type === 'step-update')
+ ? ' '
+ : '';
+
+ const messageHtml = data.message
+ ? `${escapeHtml(data.message)}
`
+ : '';
+
+ const dismissBtn = (data.type === 'complete' || data.type === 'failed')
+ ? ' '
+ : '';
+
+ const retryBtn = data.type === 'failed'
+ ? ' Retry Installation
'
+ : '';
+
+ container.innerHTML = `
+
+
+
+
${stepsHtml}
+ ${messageHtml}
+ ${retryBtn}
+
+
`;
+}
+
+function dismissSDKProgress(): void {
+ const container = document.getElementById('sdkProgressContainer');
+ if (container) { container.innerHTML = ''; }
+}
+
+// ---------------------------------------------------------------------------
+// Message handling
+// ---------------------------------------------------------------------------
+
+window.addEventListener('message', event => {
+ const message = event.data;
+
+ switch (message.command) {
+ case 'sdkListResult':
+ displaySDKList(message.data);
+ break;
+ case 'sdkListLoading':
+ showSDKListLoading();
+ break;
+ case 'sdkInstallProgress':
+ handleSDKInstallProgress(message.data);
+ break;
+ }
+});
+
+// ---------------------------------------------------------------------------
+// Expose onclick handler functions on window
+// ---------------------------------------------------------------------------
+
+function sendCommand(cmd: string): void {
+ vscode.postMessage({ command: cmd });
+}
+
+const w = window as any;
+w.listSDKs = listSDKs;
+w.installSDK = installSDK;
+w.dismissSDKProgress = dismissSDKProgress;
+w.sendCommand = sendCommand;
diff --git a/src/panels/settings_view/SettingsPanel.ts b/src/panels/settings_view/SettingsPanel.ts
index b5828048..0534e519 100644
--- a/src/panels/settings_view/SettingsPanel.ts
+++ b/src/panels/settings_view/SettingsPanel.ts
@@ -28,28 +28,28 @@ interface SettingDefinition {
const SETTINGS: SettingDefinition[] = [
{
- key: "zephyr-ide.global_directory",
+ key: "zephyr-ide.globalDirectory",
label: "Global Directory",
description: "Global directory for west workspace installation and Zephyr tools. The toolchains subdirectory is used for SDK installations unless overridden.",
type: "string",
defaultValue: null,
},
{
- key: "zephyr-ide.toolchain_directory",
+ key: "zephyr-ide.toolchainDirectory",
label: "Toolchain Directory",
description: "Directory containing Zephyr SDK installations. If not specified, defaults to the toolchains subdirectory within the global directory.",
type: "string",
defaultValue: null,
},
{
- key: "zephyr-ide.venv-folder",
+ key: "zephyr-ide.venvFolder",
label: "Virtual Environment Folder",
description: "Python virtual environment folder path. If not specified, defaults to .venv in the workspace setup path.",
type: "string",
defaultValue: null,
},
{
- key: "zephyr-ide.use_gui_config",
+ key: "zephyr-ide.useGuiConfig",
label: "Use GUI Config",
description: "Display GUI config instead of menu config in Project Tree View.",
type: "boolean",
@@ -63,7 +63,7 @@ const SETTINGS: SettingDefinition[] = [
defaultValue: false,
},
{
- key: "zephyr-ide.suppress-workspace-warning",
+ key: "zephyr-ide.suppressWorkspaceWarning",
label: "Suppress Workspace Warning",
description: "Suppress the warning about missing workspace environment variables (ZEPHYR_BASE, ZEPHYR_SDK_INSTALL_DIR).",
type: "boolean",
@@ -107,7 +107,7 @@ export class SettingsPanel {
const panel = vscode.window.createWebviewPanel(
"zephyrIDESettings",
- "Zephyr IDE Settings",
+ "Zephyr IDE: Settings",
column || vscode.ViewColumn.One,
{
enableScripts: true,
@@ -153,14 +153,19 @@ export class SettingsPanel {
const inspected = configuration.inspect(def.key);
let currentValue = configuration.get(def.key);
let scope: "default" | "user" | "workspace" = "default";
+ let userValue: boolean | string | null | undefined = undefined;
+ let workspaceValue: boolean | string | null | undefined = undefined;
if (inspected) {
- if (inspected.workspaceValue !== undefined) {
+ userValue = inspected.globalValue as typeof userValue;
+ workspaceValue = inspected.workspaceValue as typeof workspaceValue;
+
+ if (workspaceValue !== undefined) {
scope = "workspace";
- currentValue = inspected.workspaceValue;
- } else if (inspected.globalValue !== undefined) {
+ currentValue = workspaceValue;
+ } else if (userValue !== undefined) {
scope = "user";
- currentValue = inspected.globalValue;
+ currentValue = userValue;
}
}
@@ -172,6 +177,10 @@ export class SettingsPanel {
defaultValue: def.defaultValue,
currentValue: currentValue ?? def.defaultValue,
scope,
+ userValue: userValue ?? null,
+ workspaceValue: workspaceValue ?? null,
+ hasUserValue: userValue !== undefined,
+ hasWorkspaceValue: workspaceValue !== undefined,
};
});
@@ -223,6 +232,10 @@ export class SettingsPanel {
}
break;
}
+ case "openSetupPanel": {
+ await vscode.commands.executeCommand("zephyr-ide.open-setup-panel");
+ break;
+ }
}
}
@@ -270,6 +283,11 @@ export class SettingsPanel {
default
${escapeHtml(def.description)}
+
+
+
+
+
${escapeHtml(def.description)}
+
+
+
+
+
@@ -320,7 +343,12 @@ export class SettingsPanel {
-