Skip to content

sessions first time use#304893

Closed
eli-w-king wants to merge 2 commits intomainfrom
eli/sessions-first-time-use
Closed

sessions first time use#304893
eli-w-king wants to merge 2 commits intomainfrom
eli/sessions-first-time-use

Conversation

@eli-w-king
Copy link
Contributor

No description provided.

Copilot AI review requested due to automatic review settings March 25, 2026 20:16
@eli-w-king eli-w-king self-assigned this Mar 25, 2026
@vs-code-engineering vs-code-engineering bot added this to the 1.114.0 milestone Mar 25, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a first-time-use (FTU) tutorial callout to the Sessions workspace picker and introduces a developer/testing command to reset Sessions FTU-related state so the experience can be re-triggered.

Changes:

  • New WorkspacePickerCallout UI (dismiss/snooze + FAQ) rendered next to the Sessions workspace picker dropdown.
  • Workspace picker now emits explicit “show/hide” events so companion UI can react to dropdown visibility.
  • Adds a developer command to clear a set of FTU-related storage keys and reload the window for repeatable testing.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/vs/workbench/contrib/chat/browser/agentSessions/experiments/agentSessionsExperiments.contribution.ts Adds a developer/testing action to reset Agent Sessions FTU-related storage and reload.
src/vs/sessions/contrib/chat/browser/workspacePickerCallout.ts Introduces the FTU callout widget with dismiss/snooze + FAQ content backed by profile storage.
src/vs/sessions/contrib/chat/browser/workspacePicker.ts Emits onDidShowPicker / onDidHidePicker around the action widget dropdown lifecycle.
src/vs/sessions/contrib/chat/browser/newChatViewPane.ts Renders and wires the callout to workspace picker show/hide events in the welcome UI.
src/vs/sessions/contrib/chat/browser/media/workspacePickerCallout.css Adds styling/animations for the callout.
src/vs/sessions/contrib/chat/browser/media/chatWelcomePart.css Adjusts overflow/positioning to allow the callout to render outside the picker row.
src/vs/platform/actionWidget/browser/actionWidget.ts Attempts to enable interaction with the callout while the action widget is open (requires changes).

Comment on lines +44 to +55
if (this.storageService.getBoolean(STORAGE_KEY_DISMISSED, StorageScope.PROFILE, false)) {
return false;
}
if (this.storageService.getBoolean(STORAGE_KEY_SNOOZED, StorageScope.PROFILE, false)) {
const snoozeUntil = this.storageService.getNumber(STORAGE_KEY_SNOOZE_UNTIL, StorageScope.PROFILE, 0);
if (Date.now() < snoozeUntil) {
return false;
}
// Snooze expired — clear the flag
this.storageService.remove(STORAGE_KEY_SNOOZED, StorageScope.PROFILE);
this.storageService.remove(STORAGE_KEY_SNOOZE_UNTIL, StorageScope.PROFILE);
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

This new callout introduces non-trivial state behavior (dismiss vs snooze with expiry, and automatic clearing of expired snooze flags) that is easy to regress. There are existing browser unit tests for related sessions chat pieces (e.g. workspacePicker), so it would be good to add coverage for shouldShow, dismiss, and snooze expiry behavior.

Suggested change
if (this.storageService.getBoolean(STORAGE_KEY_DISMISSED, StorageScope.PROFILE, false)) {
return false;
}
if (this.storageService.getBoolean(STORAGE_KEY_SNOOZED, StorageScope.PROFILE, false)) {
const snoozeUntil = this.storageService.getNumber(STORAGE_KEY_SNOOZE_UNTIL, StorageScope.PROFILE, 0);
if (Date.now() < snoozeUntil) {
return false;
}
// Snooze expired — clear the flag
this.storageService.remove(STORAGE_KEY_SNOOZED, StorageScope.PROFILE);
this.storageService.remove(STORAGE_KEY_SNOOZE_UNTIL, StorageScope.PROFILE);
}
return this.shouldShowAtTime(Date.now());
}
/**
* Core implementation of {@link shouldShow}, parameterized by the current
* time (in milliseconds since epoch) to make the snooze expiry behavior
* easier to reason about and test.
*/
private shouldShowAtTime(now: number): boolean {
if (this.storageService.getBoolean(STORAGE_KEY_DISMISSED, StorageScope.PROFILE, false)) {
return false;
}
if (this.storageService.getBoolean(STORAGE_KEY_SNOOZED, StorageScope.PROFILE, false)) {
const snoozeUntil = this.storageService.getNumber(STORAGE_KEY_SNOOZE_UNTIL, StorageScope.PROFILE, 0);
if (now < snoozeUntil) {
return false;
}
// Snooze expired — clear the flag so subsequent calls don't need to
// perform time comparisons against an outdated value.
this.storageService.remove(STORAGE_KEY_SNOOZED, StorageScope.PROFILE);
this.storageService.remove(STORAGE_KEY_SNOOZE_UNTIL, StorageScope.PROFILE);
}

Copilot uses AI. Check for mistakes.
Comment on lines +306 to +311
import { IStorageService, StorageScope } from '../../../../../../platform/storage/common/storage.js';
import { IHostService } from '../../../../../services/host/browser/host.js';
import { Categories } from '../../../../../../platform/action/common/actionCommonCategories.js';
import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js';
import { IEditorGroupsService } from '../../../../../services/editor/common/editorGroupsService.js';

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

New imports are introduced mid-file (after statements). In this codebase, module imports are expected to be grouped at the top for readability and to avoid order-dependent side effects. Please move these imports to the top import section (and keep sorting consistent with the rest of the file).

Copilot uses AI. Check for mistakes.
Comment on lines +327 to +332
// APPLICATION-scoped keys
storageService.remove('workbench.agentsession.welcomeComplete', StorageScope.APPLICATION);
storageService.remove('agentSessionsWelcome.privacyNoticeDismissed', StorageScope.APPLICATION);
storageService.remove('chat.welcomeViewPrefill', StorageScope.APPLICATION);
storageService.remove('agentSessions.pinnedTaskLabels', StorageScope.APPLICATION);

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

This reset command hard-codes a long list of storage keys across multiple components. That’s brittle (keys/scopes can change independently) and easy to miss when new FTU-related keys are added. Consider centralizing these keys behind a single reset API (e.g. owned by the sessions FTU components) or reusing exported constants so the reset stays correct when storage keys evolve.

Copilot uses AI. Check for mistakes.
Comment on lines +140 to +146
if (user === 'workspacePicker') {
// Allow interactions with the sessions workspace tutorial callout,
// which is rendered alongside the action widget while this picker is open.
block.style.pointerEvents = 'none';
} else {
renderDisposables.add(dom.addDisposableListener(block, dom.EventType.MOUSE_DOWN, e => e.stopPropagation()));
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Setting pointerEvents = 'none' on the full-screen .context-view-block means clicks outside the action widget will reach (and potentially trigger) the underlying UI before/while the context view hides (ContextView hides on captured click but does not cancel the event). This can cause accidental interactions while the workspace picker is open. Please keep outside clicks blocked and instead make the tutorial callout clickable by rendering it within the context view layer (or otherwise ensuring it sits above the block) rather than disabling pointer events on the block.

Suggested change
if (user === 'workspacePicker') {
// Allow interactions with the sessions workspace tutorial callout,
// which is rendered alongside the action widget while this picker is open.
block.style.pointerEvents = 'none';
} else {
renderDisposables.add(dom.addDisposableListener(block, dom.EventType.MOUSE_DOWN, e => e.stopPropagation()));
}
renderDisposables.add(dom.addDisposableListener(block, dom.EventType.MOUSE_DOWN, e => e.stopPropagation()));

Copilot uses AI. Check for mistakes.
Comment on lines +140 to +145
if (user === 'workspacePicker') {
// Allow interactions with the sessions workspace tutorial callout,
// which is rendered alongside the action widget while this picker is open.
block.style.pointerEvents = 'none';
} else {
renderDisposables.add(dom.addDisposableListener(block, dom.EventType.MOUSE_DOWN, e => e.stopPropagation()));
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Special-casing behavior based on the magic user === 'workspacePicker' string is fragile (easy to mistype and not discoverable to other callers). Consider using a typed options argument (e.g. { allowCompanionElementSelector: ... } / { allowOutsideInteractionWith: ... }) or a shared constant/enum so this coupling is explicit and maintainable.

Copilot uses AI. Check for mistakes.

this._renderFaqItem(faq,
localize('faqCloneQ', "How do I clone a repo?"),
localize('faqCloneA', "Use the command palette ({0}) and run \"Git: Clone\", then paste a repository URL. The cloned folder will appear in your file explorer and can be opened here.", 'Cmd+Shift+P'),
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The FAQ answer hard-codes the Command Palette shortcut as "Cmd+Shift+P", which is incorrect on non-macOS and can diverge from user-customized keybindings. Prefer either omitting the shortcut, using a localized generic phrase, or resolving the actual keybinding label via the keybinding service.

Suggested change
localize('faqCloneA', "Use the command palette ({0}) and run \"Git: Clone\", then paste a repository URL. The cloned folder will appear in your file explorer and can be opened here.", 'Cmd+Shift+P'),
localize('faqCloneA', "Use the Command Palette to run \"Git: Clone\", then paste a repository URL. The cloned folder will appear in your file explorer and can be opened here."),

Copilot uses AI. Check for mistakes.
@eli-w-king eli-w-king closed this Mar 25, 2026
@eli-w-king eli-w-king deleted the eli/sessions-first-time-use branch March 25, 2026 22:45
@eli-w-king eli-w-king restored the eli/sessions-first-time-use branch March 25, 2026 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants