From e5cd41cdd9f8c25f381cb49fa49754fbd3933728 Mon Sep 17 00:00:00 2001 From: Turgay Nalbant <182372382+tnalbant@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:12:12 +0300 Subject: [PATCH 1/2] fix(vscode): show every namespace as selected when the selection is empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hovering the CALM Hub status bar item with `calm.hub.selectedNamespaces` empty or unset reported "0 selected" and marked every namespace with ○, even though an empty selection means "all namespaces" everywhere else. That rule was written inline in CanvasPanel.sendAssets(). It now lives in resolveSelectedNamespaces(), which the canvas panel and the status bar both call, so the two cannot drift apart again. The status bar text is unchanged: an empty selection still shows (N). Fixes #3493 --- .../extension/services/hub-status-bar.test.ts | 9 +++++++ .../src/extension/services/hub-status-bar.ts | 7 +++-- .../services/namespace-selection.test.ts | 27 +++++++++++++++++++ .../extension/services/namespace-selection.ts | 15 +++++++++++ .../src/extension/webview/canvas-panel.ts | 6 ++--- 5 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 calm-plugins/vscode/src/extension/services/namespace-selection.test.ts create mode 100644 calm-plugins/vscode/src/extension/services/namespace-selection.ts diff --git a/calm-plugins/vscode/src/extension/services/hub-status-bar.test.ts b/calm-plugins/vscode/src/extension/services/hub-status-bar.test.ts index 56fb8bddd1..bf217374be 100644 --- a/calm-plugins/vscode/src/extension/services/hub-status-bar.test.ts +++ b/calm-plugins/vscode/src/extension/services/hub-status-bar.test.ts @@ -96,6 +96,15 @@ describe('HubStatusBar', () => { expect(mockItem.tooltip).toContain('○ ns2'); expect(mockItem.tooltip).toContain('✓ ns3'); }); + + it('marks every namespace as selected when the selection is empty', () => { + statusBar.setState('connected', 2); + statusBar.setAvailableAndSelected(['alpha', 'beta'], []); + expect(mockItem.text).toBe('$(cloud) CALM Hub (2)'); + expect(mockItem.tooltip).toContain('Namespaces (2 selected / 2 available)'); + expect(mockItem.tooltip).toContain('✓ alpha'); + expect(mockItem.tooltip).toContain('✓ beta'); + }); }); describe('dispose', () => { diff --git a/calm-plugins/vscode/src/extension/services/hub-status-bar.ts b/calm-plugins/vscode/src/extension/services/hub-status-bar.ts index afaf492aab..2ad8df32c7 100644 --- a/calm-plugins/vscode/src/extension/services/hub-status-bar.ts +++ b/calm-plugins/vscode/src/extension/services/hub-status-bar.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { resolveSelectedNamespaces } from './namespace-selection'; export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'error'; @@ -64,11 +65,13 @@ export class HubStatusBar { const countLabel = selCount > 0 ? `${selCount}/${availCount}` : `${availCount}`; this.statusBarItem.text = `$(cloud) CALM Hub (${countLabel})`; + // An empty selection means "all namespaces", so the tooltip resolves it (#3493). + const selected = resolveSelectedNamespaces(this.availableNamespaces, this.selectedNamespaces); const nsList = this.availableNamespaces.map((ns) => - this.selectedNamespaces.includes(ns) ? ` ✓ ${ns}` : ` ○ ${ns}` + selected.includes(ns) ? ` ✓ ${ns}` : ` ○ ${ns}` ).join('\n'); this.statusBarItem.tooltip = (this.hubUrl ? `${this.hubUrl}\n\n` : '') + - `Namespaces (${selCount} selected / ${availCount} available):\n` + + `Namespaces (${selected.length} selected / ${availCount} available):\n` + nsList + '\n\nClick to manage'; this.statusBarItem.backgroundColor = undefined; diff --git a/calm-plugins/vscode/src/extension/services/namespace-selection.test.ts b/calm-plugins/vscode/src/extension/services/namespace-selection.test.ts new file mode 100644 index 0000000000..79ab454d13 --- /dev/null +++ b/calm-plugins/vscode/src/extension/services/namespace-selection.test.ts @@ -0,0 +1,27 @@ +import { describe, it, expect } from 'vitest'; +import { resolveSelectedNamespaces } from './namespace-selection'; + +describe('resolveSelectedNamespaces', () => { + const available = ['alpha', 'beta', 'gamma']; + + it('treats an empty selection as "all namespaces"', () => { + expect(resolveSelectedNamespaces(available, [])).toEqual(available); + }); + + it('treats an unset selection as "all namespaces"', () => { + expect(resolveSelectedNamespaces(available, undefined)).toEqual(available); + expect(resolveSelectedNamespaces(available, null)).toEqual(available); + }); + + it('keeps only the selected namespaces', () => { + expect(resolveSelectedNamespaces(available, ['gamma', 'alpha'])).toEqual(['alpha', 'gamma']); + }); + + it('drops names that the hub no longer lists', () => { + expect(resolveSelectedNamespaces(available, ['alpha', 'gone'])).toEqual(['alpha']); + }); + + it('returns nothing when the hub lists no namespaces', () => { + expect(resolveSelectedNamespaces([], [])).toEqual([]); + }); +}); diff --git a/calm-plugins/vscode/src/extension/services/namespace-selection.ts b/calm-plugins/vscode/src/extension/services/namespace-selection.ts new file mode 100644 index 0000000000..31fb07b980 --- /dev/null +++ b/calm-plugins/vscode/src/extension/services/namespace-selection.ts @@ -0,0 +1,15 @@ +/** + * CALM Hub treats an empty namespace selection as "every namespace". VS Code also + * reports an unset string array as `[]`, so `undefined`, `null` and `[]` all mean + * the same thing. The canvas panel and the status bar both resolve the selection + * through this function so the rule lives in one place (#3493). + */ +export function resolveSelectedNamespaces( + available: readonly string[], + selection: readonly string[] | null | undefined +): string[] { + if (!selection || selection.length === 0) { + return [...available]; + } + return available.filter((namespace) => selection.includes(namespace)); +} diff --git a/calm-plugins/vscode/src/extension/webview/canvas-panel.ts b/calm-plugins/vscode/src/extension/webview/canvas-panel.ts index b64d8af6e7..deb60315e9 100644 --- a/calm-plugins/vscode/src/extension/webview/canvas-panel.ts +++ b/calm-plugins/vscode/src/extension/webview/canvas-panel.ts @@ -8,6 +8,7 @@ import { DiagramExportService } from '../services/diagram-export-service'; import { HubClient, describeHubError } from '../services/hub-client'; import { HubAssetService, type HubNamespace } from '../services/hub-asset-service'; import { ControlAssetService, LOCAL_DOMAIN } from '../services/control-asset-service'; +import { resolveSelectedNamespaces } from '../services/namespace-selection'; import { ShaCacheService } from '../services/sha-cache-service'; import { SvgImportService } from '../services/svg-import'; import { @@ -346,10 +347,7 @@ export class CanvasPanel { const allNs = this.hubAssetService?.getNamespaces().map(ns => ns.name) ?? []; const userSelection = vscode.workspace.getConfiguration('calm.hub').get('selectedNamespaces'); - // VS Code defaults unset arrays to []. Treat null/undefined/[] as "show all". - const selectedNs = (userSelection && userSelection.length > 0) - ? allNs.filter(ns => userSelection.includes(ns)) - : allNs; + const selectedNs = resolveSelectedNamespaces(allNs, userSelection); const hubBlocks = this.hubAssetService?.getAllStandardNodes(selectedNs) ?? []; const hubPatterns = this.hubAssetService?.getAllPatterns(selectedNs) ?? []; From c63ae3fd10de06702d0667f0f6321f59d7a427c7 Mon Sep 17 00:00:00 2001 From: Turgay Nalbant <182372382+tnalbant@users.noreply.github.com> Date: Tue, 27 Oct 2026 12:19:12 +0300 Subject: [PATCH 2/2] refactor(vscode): describe the namespace rule without a caller list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review feedback on #3521: CALM Hub has no namespace selection of its own — the filter is the plugin's `calm.hub.selectedNamespaces` setting — and listing the callers in a docstring goes stale. The comment now names the setting instead. Refs #3493 --- .../src/extension/services/namespace-selection.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/calm-plugins/vscode/src/extension/services/namespace-selection.ts b/calm-plugins/vscode/src/extension/services/namespace-selection.ts index 31fb07b980..cf1d6e0128 100644 --- a/calm-plugins/vscode/src/extension/services/namespace-selection.ts +++ b/calm-plugins/vscode/src/extension/services/namespace-selection.ts @@ -1,8 +1,10 @@ /** - * CALM Hub treats an empty namespace selection as "every namespace". VS Code also - * reports an unset string array as `[]`, so `undefined`, `null` and `[]` all mean - * the same thing. The canvas panel and the status bar both resolve the selection - * through this function so the rule lives in one place (#3493). + * Resolve the `calm.hub.selectedNamespaces` setting against the namespaces the + * Hub lists. + * + * CALM Hub has no namespace selection of its own: the filter is this plugin's + * setting. VS Code reports an unset string array as `[]`, so `undefined`, `null` + * and `[]` all mean "every namespace". */ export function resolveSelectedNamespaces( available: readonly string[],