diff --git a/apps/desktop/features/spaces/background.js b/apps/desktop/features/spaces/background.js index 49907e18..895be67a 100644 --- a/apps/desktop/features/spaces/background.js +++ b/apps/desktop/features/spaces/background.js @@ -498,6 +498,7 @@ async function bootstrapDefaultSpace() { activeSpaceId = tag.id; activeSpaceName = spaceName; activeSpaceColor = tag.color; + refreshSpaceNameCommands(); await declareActiveWorkspaceForNewWindows(tag.id, spaceName, windowColor); await adoptOpenWindowsInto(tag, spaceName, windowColor); @@ -567,6 +568,7 @@ async function enterWorkspace(tag, spaceName) { activeSpaceId = tag.id; activeSpaceName = spaceName; activeSpaceColor = tag.color; + refreshSpaceNameCommands(); if (isDefault) { // The default shows no chrome — see updateBorderVisibility()'s matching @@ -1112,7 +1114,7 @@ async function refreshSpaceNameCommands() { spaceNameRefreshQueued = false; try { const tags = await listSpaceTags(); - spaceNameSuggestions = spaceNameOptions(tags, await countWindowsBySpace(tags)); + spaceNameSuggestions = spaceNameOptions(tags, await countWindowsBySpace(tags), activeSpaceId); publishSpaceNameCommands(); } catch (err) { console.error('[ext:spaces] Failed to refresh space name suggestions:', err); diff --git a/apps/desktop/features/spaces/config.js b/apps/desktop/features/spaces/config.js index 2bc6fd97..18dd1a16 100644 --- a/apps/desktop/features/spaces/config.js +++ b/apps/desktop/features/spaces/config.js @@ -301,18 +301,24 @@ const validateNewSpaceName = (name, existingNames, defaultName) => { * Command-panel suggestions for a space-name argument: one entry per space tag, * shown by display name (no internal tag prefix), sorted by name. `counts` * maps a tag id to its window count; a space with no known count gets no + * subtitle. The space whose tag id is `currentId` is marked "current" in its * subtitle. */ -const spaceNameOptions = (tags, counts) => +const spaceNameOptions = (tags, counts, currentId) => (Array.isArray(tags) ? tags : []) .filter((t) => t && isSpaceTag(t.name)) - .map((t) => ({ name: getSpaceDisplayName(t), count: counts?.[t.id] })) + .map((t) => ({ name: getSpaceDisplayName(t), count: counts?.[t.id], current: !!currentId && t.id === currentId })) .sort((a, b) => a.name.localeCompare(b.name)) - .map(({ name, count }) => ({ - value: name, - title: name, - ...(typeof count === 'number' ? { subtitle: count === 0 ? 'no windows' : formatWindowCount(count) } : {}), - })); + .map(({ name, count, current }) => { + const parts = []; + if (typeof count === 'number') parts.push(count === 0 ? 'no windows' : formatWindowCount(count)); + if (current) parts.push('current'); + return { + value: name, + title: name, + ...(parts.length ? { subtitle: parts.join(' · ') } : {}), + }; + }); /** * The space name a command message carries: the search text, else the argument diff --git a/apps/desktop/tests/unit/spaces-commands-refresh.test.js b/apps/desktop/tests/unit/spaces-commands-refresh.test.js index 8435c304..a6f94ef8 100644 --- a/apps/desktop/tests/unit/spaces-commands-refresh.test.js +++ b/apps/desktop/tests/unit/spaces-commands-refresh.test.js @@ -50,7 +50,7 @@ globalThis.window = { }, }; -const { default: feature } = await import(join(__dirname, '..', '..', 'features', 'spaces', 'background.js')); +const { default: feature, __test } = await import(join(__dirname, '..', '..', 'features', 'spaces', 'background.js')); const settle = () => new Promise((resolve) => setImmediate(resolve)); @@ -151,6 +151,29 @@ describe('space-name commands show window counts', () => { }); }); +describe('space-name commands mark the current space', () => { + const subtitleOf = (commandName, value) => [...registrations].reverse() + .find((r) => r.name === commandName)?.params?.[0]?.options.find((o) => o.value === value)?.subtitle; + + it('adds "current" to the active space subtitle and moves it when the active space changes', async () => { + tags = [{ id: 't-default', name: 'space:default' }, { id: 't-work', name: 'space:work' }]; + liveWindows = { 't-work': ['w1', 'w2'] }; + __test.setActiveSpaceId('t-work'); + await handlers.get('spaces:membership-changed')({ windowId: 'w2', spaceId: 't-work' }); + await settle(); + assert.equal(subtitleOf('switch space', 'work'), '2 windows · current'); + assert.equal(subtitleOf('move to space', 'work'), '2 windows · current'); + assert.equal(subtitleOf('switch space', 'default'), 'no windows'); + + __test.setActiveSpaceId('t-default'); + await handlers.get('spaces:membership-changed')({ windowId: 'w2', spaceId: 't-work' }); + await settle(); + assert.equal(subtitleOf('switch space', 'work'), '2 windows'); + assert.match(subtitleOf('switch space', 'default'), /current$/); + liveWindows = {}; + }); +}); + describe('space-name commands name a create path', () => { it('only switch space offers to create a space that does not exist', () => { const createCommandOf = (name) => [...registrations].reverse().find((r) => r.name === name)?.params?.[0]?.createCommand;