Skip to content

Commit

Permalink
Improves Main Interpreter Picker (#6363)
Browse files Browse the repository at this point in the history
Adds more useable interface for primary interpreter picker.

Addresses #6150, #3596, #2143


https://github.com/user-attachments/assets/a9aa05ae-3c51-4a3b-bbec-5b0add8a9373


### Release Notes

<!--
Optionally, replace `N/A` with text to be included in the next release
notes.
The `N/A` bullets are ignored. If you refer to one or more Positron
issues,
these issues are used to collect information about the feature or
bugfix, such
as the relevant language pack as determined by Github labels of type
`lang: `.
  The note will automatically be tagged with the language.

These notes are typically filled by the Positron team. If you are an
external
  contributor, you may ignore this section.
-->

#### New Features

- Adds action to open Quick Pick to allow user to choose active runtime
session, or to create a new one
- Replaces main interpreter picker dropdown with button to trigger Quick
Pick above
- Modifies Positron action bar button to allow display of custom icon
(vs. only Codicons as was previously) to support language icons in new
interpreter button

#### Bug Fixes

- N/A


### QA Notes

<!--
  Add additional information for QA on how to validate the change,
  paying special attention to the level of risk, adjacent areas that
  could be affected by the change, and any important contextual
  information not present in the linked issues.
-->

e2e: @:interpreter

---------

Signed-off-by: Sam Clark <samclark2015@users.noreply.github.com>
Co-authored-by: Dhruvi Sompura <dhruvi.sompura@posit.co>
  • Loading branch information
samclark2015 and dhruvisompura authored Feb 21, 2025
1 parent 97c6800 commit c4908fd
Show file tree
Hide file tree
Showing 5 changed files with 332 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
background: var(--positronActionBar-hoverBackground);
}

.action-bar-button.border {
height: 26px;
background-color: var(--positronActionBar-textInputBackground);
border: 1px solid var(--positronActionBar-border);
}

.action-bar-button:hover {
background: var(--positronActionBar-hoverBackground);
}
Expand Down Expand Up @@ -68,6 +74,10 @@
text-overflow: ellipsis;
}

.action-bar-button.border .action-bar-button-text {
margin-right: 6px;
}

.disabled .action-bar-button-text {
color: var(--positronActionBar-disabledForeground);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,24 @@ import { optionalBoolean, optionalValue, positronClassNames } from '../../../../
/**
* ActionBarButtonProps interface.
*/
export interface ActionBarButtonProps {
readonly fadeIn?: boolean;

type ActionBarButtonIconProps = {
readonly iconId?: string;
readonly iconFontSize?: number;

readonly iconImageSrc?: never;
readonly iconHeight?: never;
readonly iconWidth?: never;
} | {
readonly iconImageSrc?: string;
readonly iconHeight?: number;
readonly iconWidth?: number;

readonly iconId?: never;
readonly iconFontSize?: never;
};
export type ActionBarButtonProps = {
readonly fadeIn?: boolean;
readonly text?: string;
readonly maxTextWidth?: number;
readonly align?: 'left' | 'right';
Expand All @@ -31,12 +45,13 @@ export interface ActionBarButtonProps {
readonly ariaLabel?: string;
readonly dropdownAriaLabel?: string;
readonly dropdownIndicator?: 'disabled' | 'enabled' | 'enabled-split';
readonly border?: boolean;
readonly mouseTrigger?: MouseTrigger;
readonly onMouseEnter?: () => void;
readonly onMouseLeave?: () => void;
readonly onPressed?: () => void;
readonly onDropdownPressed?: () => void;
}
} & ActionBarButtonIconProps

/**
* ActionBarButton component.
Expand Down Expand Up @@ -89,23 +104,43 @@ export const ActionBarButton = forwardRef<
style={iconStyle}
/>
)}
{props.text && (
{props.iconImageSrc && (
<div
className='action-bar-button-text'
style={{
marginLeft: props.iconId ? 0 : 4,
maxWidth: optionalValue(props.maxTextWidth, 'none')
}}
>
{props.text}
</div>
)}
{props.dropdownIndicator === 'enabled' && (
<div className='action-bar-button-drop-down-container'>
<div className='action-bar-button-drop-down-arrow codicon codicon-positron-drop-down-arrow' />
className={positronClassNames(
'action-bar-button-icon',
)}
style={iconStyle}>
<img
src={props.iconImageSrc}
style={{
height: props.iconHeight ?? 16,
width: props.iconWidth ?? 16
}}
/>
</div>
)}
</div>
)
}
{
props.text && (
<div
className='action-bar-button-text'
style={{
marginLeft: (props.iconId || props.iconImageSrc) ? 0 : 4,
maxWidth: optionalValue(props.maxTextWidth, 'none')
}}
>
{props.text}
</div>
)
}
{
props.dropdownIndicator === 'enabled' && (
<div className='action-bar-button-drop-down-container'>
<div className='action-bar-button-drop-down-arrow codicon codicon-positron-drop-down-arrow' />
</div>
)
}
</div >
);
};

Expand All @@ -118,7 +153,8 @@ export const ActionBarButton = forwardRef<
className={positronClassNames(
'action-bar-button',
{ 'fade-in': optionalBoolean(props.fadeIn) },
{ 'checked': optionalBoolean(props.checked) }
{ 'checked': optionalBoolean(props.checked) },
{ 'border': optionalBoolean(props.border) }
)}
disabled={props.disabled}
hoverManager={context.hoverManager}
Expand All @@ -137,7 +173,8 @@ export const ActionBarButton = forwardRef<
<div className={positronClassNames(
'action-bar-button',
{ 'fade-in': optionalBoolean(props.fadeIn) },
{ 'checked': optionalBoolean(props.checked) }
{ 'checked': optionalBoolean(props.checked) },
{ 'border': optionalBoolean(props.border) }
)}>
<Button
ref={buttonRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ActionBarButton, ActionBarButtonProps } from './actionBarButton.js';
/**
* ActionBarCommandButtonProps interface.
*/
interface ActionBarCommandButtonProps extends ActionBarButtonProps {
type ActionBarCommandButtonProps = ActionBarButtonProps & {
readonly commandId: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ import { PositronModalReactRenderer } from '../../../positronModalReactRenderer/
import { usePositronTopActionBarContext } from '../positronTopActionBarContext.js';
import { ILanguageRuntimeMetadata, LanguageRuntimeSessionMode } from '../../../../services/languageRuntime/common/languageRuntimeService.js';
import { InterpretersManagerModalPopup } from '../interpretersManagerModalPopup/interpretersManagerModalPopup.js';
import { multipleConsoleSessionsFeatureEnabled, USE_POSITRON_MULTIPLE_CONSOLE_SESSIONS_CONFIG_KEY } from '../../../../services/runtimeSession/common/positronMultipleConsoleSessionsFeatureFlag.js';
import { ActionBarCommandButton } from '../../../../../platform/positronActionBar/browser/components/actionBarCommandButton.js';
import { CommandCenter } from '../../../../../platform/commandCenter/common/commandCenter.js';
import { ILanguageRuntimeSession } from '../../../../services/runtimeSession/common/runtimeSessionService.js';
import { localize } from '../../../../../nls.js';

const runtimePickerAction = 'workbench.action.language.runtime.openActivePicker';

/**
* TopActionBarInterpretersManagerProps interface.
Expand All @@ -30,7 +37,7 @@ interface TopActionBarInterpretersManagerProps {
* TopActionBarInterpretersManager component.
* @returns The rendered component.
*/
export const TopActionBarInterpretersManager = (props: TopActionBarInterpretersManagerProps) => {
export const TopActionBarInterpretersManager_Legacy = (props: TopActionBarInterpretersManagerProps) => {
// Context hooks.
const context = usePositronTopActionBarContext();

Expand Down Expand Up @@ -140,3 +147,75 @@ export const TopActionBarInterpretersManager = (props: TopActionBarInterpretersM
</div>
);
};

export const TopActionBarInterpretersManager_New = (props: TopActionBarInterpretersManagerProps) => {
const context = usePositronTopActionBarContext();

const [activeSession, setActiveSession] = useState<ILanguageRuntimeSession>();

// Main useEffect.
useEffect(() => {
// Create the disposable store for cleanup.
const disposableStore = new DisposableStore();

// Add the onDidChangeForegroundSession event handler.
disposableStore.add(
context.runtimeSessionService.onDidChangeForegroundSession(session => {
if (session?.metadata.sessionMode === LanguageRuntimeSessionMode.Console) {
setActiveSession(
context.runtimeSessionService.foregroundSession);
}
})
);

// Return the cleanup function that will dispose of the disposables.
return () => disposableStore.dispose();
}, [context.runtimeSessionService]);

const labelText = activeSession?.runtimeMetadata?.runtimeName ?? localize('positron.interpretersManager.chooseSession', 'Choose Session');

return (
<ActionBarCommandButton
ariaLabel={CommandCenter.title(runtimePickerAction)}
border={true}
commandId={runtimePickerAction}
text={labelText}
{
...(
activeSession
? { iconImageSrc: `data:image/svg+xml;base64,${activeSession?.runtimeMetadata.base64EncodedIconSvg}` }
: { iconId: 'arrow-swap' }
)
}
/>
);
}

export const TopActionBarInterpretersManager = (props: TopActionBarInterpretersManagerProps) => {
const context = usePositronTopActionBarContext();

const [newInterpretersManager, setNewInterpretersManager] = useState(
multipleConsoleSessionsFeatureEnabled(context.configurationService)
);

useEffect(() => {
const disposableStore = new DisposableStore();
disposableStore.add(
context.configurationService.onDidChangeConfiguration((e) => {
if (e.affectedKeys.has(USE_POSITRON_MULTIPLE_CONSOLE_SESSIONS_CONFIG_KEY)) {
setNewInterpretersManager(
multipleConsoleSessionsFeatureEnabled(context.configurationService)
);
}
})
);

return () => disposableStore.dispose();
}, [context.configurationService]);

return (
newInterpretersManager
? <TopActionBarInterpretersManager_New {...props} />
: <TopActionBarInterpretersManager_Legacy {...props} />
);
}
Loading

0 comments on commit c4908fd

Please sign in to comment.