Skip to content

Commit bc3737c

Browse files
seeMdhruvisompuramidleman
authored
PositroNB: Restart kernel button (#9898)
Add the Restart Kernel button to Positron notebooks, addresses #9789. The button lives in the new Kernel Actions menu, addressing #9722. The Kernel Actions button shows the runtime status and name, matching the style used in the console. <img width="520" height="242" alt="image" src="https://github.com/user-attachments/assets/e2313450-0361-4466-aa2d-95f54acc5852" /> Also removed the titles from Run All and Clear Outputs buttons, and moved + Code and + Markdown to the left. <img width="1126" height="242" alt="image" src="https://github.com/user-attachments/assets/fc014997-e69c-4d68-94c3-fb2050359576" /> ### Release Notes #### New Features - N/A #### Bug Fixes - N/A ### QA Notes @:notebooks --------- Signed-off-by: Wasim Lorgat <[email protected]> Co-authored-by: Dhruvi Sompura <[email protected]> Co-authored-by: Marie Idleman <[email protected]>
1 parent ec93dc7 commit bc3737c

30 files changed

+508
-196
lines changed

src/vs/platform/actions/common/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export class MenuId {
7878
static readonly EditorContextPeek = new MenuId('EditorContextPeek');
7979
static readonly EditorContextShare = new MenuId('EditorContextShare');
8080
// --- Start Positron ---
81+
static readonly PositronNotebookKernelSubmenu = new MenuId('PositronNotebookKernelSubmenu');
8182
static readonly EditorActionsLeft = new MenuId('EditorActionsLeft');
8283
static readonly EditorActionsRight = new MenuId('EditorActionsRight');
8384
// --- End Positron ---

src/vs/platform/positronActionBar/browser/components/actionBarButton.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ export const ActionBarButton = forwardRef<
174174
{props.label}
175175
</div>
176176
}
177+
{props.children}
177178
{props.dropdownIndicator === 'enabled' &&
178179
<div className='action-bar-button-drop-down-container'>
179180
<div className='action-bar-button-drop-down-arrow codicon codicon-positron-drop-down-arrow' />
@@ -205,7 +206,6 @@ export const ActionBarButton = forwardRef<
205206
onPressed={props.onPressed}
206207
>
207208
<ActionBarButtonFace />
208-
{props.children}
209209
</Button>
210210
);
211211
} else {
@@ -242,7 +242,6 @@ export const ActionBarButton = forwardRef<
242242
>
243243
<div className='action-bar-button-drop-down-arrow codicon codicon-positron-drop-down-arrow' />
244244
</Button>
245-
{props.children}
246245
</div>
247246
);
248247
}

src/vs/platform/positronActionBar/browser/components/actionBarMenuButton.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import './actionBarMenuButton.css';
88

99
// React.
10-
import React, { useEffect, useRef, useState } from 'react';
10+
import React, { PropsWithChildren, useEffect, useRef, useState } from 'react';
1111

1212
// Other dependencies.
1313
import { ActionBarButton } from './actionBarButton.js';
@@ -48,7 +48,7 @@ interface ActionBarMenuButtonProps {
4848
* @param props An ActionBarMenuButtonProps that contains the component properties.
4949
* @returns The rendered component.
5050
*/
51-
export const ActionBarMenuButton = (props: ActionBarMenuButtonProps) => {
51+
export const ActionBarMenuButton = (props: PropsWithChildren<ActionBarMenuButtonProps>) => {
5252
// Context hooks.
5353
const services = usePositronReactServicesContext();
5454
const positronActionBarContext = usePositronActionBarContext();
@@ -74,15 +74,16 @@ export const ActionBarMenuButton = (props: ActionBarMenuButtonProps) => {
7474
}
7575
}, [positronActionBarContext.menuShowing]);
7676

77+
const { actions: getActions } = props;
7778
const getMenuActions = React.useCallback(async () => {
78-
const actions = await props.actions();
79+
const actions = await getActions();
7980
const defaultAction = actions.find(action => action.checked);
8081

8182
setDefaultAction(defaultAction);
8283
setActions(actions);
8384

8485
return actions;
85-
}, [props]);
86+
}, [getActions]);
8687

8788
useEffect(() => {
8889
getMenuActions();

src/vs/platform/positronActionBar/browser/components/actionBarWidget.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
.action-bar-widget {
7-
padding-inline: 6px;
8-
}
9-
106
/**
117
* Error indicator shown when a widget fails to render.
128
* Displays a small error icon with tooltip explaining the failure.

src/vs/workbench/contrib/positronConsole/browser/components/consoleInstanceState.tsx

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,17 @@ import React, { useEffect, useState } from 'react';
1212
// Other dependencies.
1313
import { IPositronConsoleInstance, PositronConsoleState } from '../../../../services/positronConsole/browser/interfaces/positronConsoleService.js';
1414
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
15-
16-
const enum StatusIconClassName {
17-
ACTIVE = 'codicon-positron-status-active',
18-
DISCONNECTED = 'codicon-positron-status-disconnected',
19-
IDLE = 'codicon-positron-status-idle'
20-
}
21-
22-
const statusIconClassNameToColor = {
23-
[StatusIconClassName.ACTIVE]: 'var(--vscode-positronConsole-stateIconActive)',
24-
[StatusIconClassName.DISCONNECTED]: 'var(--vscode-positronConsole-stateIconDisconnected)',
25-
[StatusIconClassName.IDLE]: 'var(--vscode-positronConsole-stateIconIdle)'
26-
}
27-
28-
const consoleStateToStatusIcon = {
29-
[PositronConsoleState.Uninitialized]: StatusIconClassName.DISCONNECTED,
30-
[PositronConsoleState.Disconnected]: StatusIconClassName.DISCONNECTED,
31-
[PositronConsoleState.Starting]: StatusIconClassName.ACTIVE,
32-
[PositronConsoleState.Busy]: StatusIconClassName.ACTIVE,
33-
[PositronConsoleState.Ready]: StatusIconClassName.IDLE,
34-
[PositronConsoleState.Offline]: StatusIconClassName.DISCONNECTED,
35-
[PositronConsoleState.Exiting]: StatusIconClassName.ACTIVE,
36-
[PositronConsoleState.Exited]: StatusIconClassName.DISCONNECTED
15+
import { RuntimeStatus, RuntimeStatusIcon } from './runtimeStatus.js';
16+
17+
const consoleStateToRuntimeStatus = {
18+
[PositronConsoleState.Uninitialized]: RuntimeStatus.Disconnected,
19+
[PositronConsoleState.Disconnected]: RuntimeStatus.Disconnected,
20+
[PositronConsoleState.Starting]: RuntimeStatus.Active,
21+
[PositronConsoleState.Busy]: RuntimeStatus.Active,
22+
[PositronConsoleState.Ready]: RuntimeStatus.Idle,
23+
[PositronConsoleState.Offline]: RuntimeStatus.Disconnected,
24+
[PositronConsoleState.Exiting]: RuntimeStatus.Active,
25+
[PositronConsoleState.Exited]: RuntimeStatus.Disconnected
3726
};
3827

3928
interface ConsoleInstanceStateProps {
@@ -55,13 +44,9 @@ export const ConsoleInstanceState = ({ positronConsoleInstance }: ConsoleInstanc
5544
return () => disposableStore.dispose();
5645
}, [positronConsoleInstance]);
5746

58-
const icon = consoleStateToStatusIcon[consoleState];
59-
const color = statusIconClassNameToColor[icon];
47+
const runtimeStatus = consoleStateToRuntimeStatus[consoleState];
6048

6149
return (
62-
<span
63-
className={`codicon ${icon}`}
64-
style={{ color }}
65-
/>
50+
<RuntimeStatusIcon status={runtimeStatus} />
6651
);
6752
}

src/vs/workbench/contrib/positronConsole/browser/components/consoleTabList.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { PositronConsoleTabFocused } from '../../../../common/contextkeys.js';
2222
import { usePositronReactServicesContext } from '../../../../../base/browser/positronReactRendererContext.js';
2323
import { LanguageRuntimeSessionMode } from '../../../../services/languageRuntime/common/languageRuntimeService.js';
2424
import { basename } from '../../../../../base/common/path.js';
25+
import { RuntimeIcon } from './runtimeIcon.js';
2526

2627
/**
2728
* The minimum width required for the delete action to be displayed on the console tab.
@@ -378,16 +379,10 @@ const ConsoleTab = ({ positronConsoleInstance, width, onChangeSession }: Console
378379
onMouseDown={handleMouseDown}
379380
>
380381
<ConsoleInstanceState positronConsoleInstance={positronConsoleInstance} />
381-
{
382-
!isNotebookSession &&
383-
<img
384-
className='icon'
385-
src={`data:image/svg+xml;base64,${positronConsoleInstance.runtimeMetadata.base64EncodedIconSvg}`}
386-
/>
387-
}
388-
{isNotebookSession &&
389-
<span className='codicon codicon-notebook icon'></span>
390-
}
382+
<RuntimeIcon
383+
base64EncodedIconSvg={positronConsoleInstance.runtimeMetadata.base64EncodedIconSvg}
384+
sessionMode={positronConsoleInstance.sessionMetadata.sessionMode}
385+
/>
391386
{isRenamingSession ? (
392387
<input
393388
ref={inputRef}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
3+
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
4+
*--------------------------------------------------------------------------------------------*/
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
3+
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
// CSS.
7+
import './runtimeStatus.css';
8+
9+
// React.
10+
import React from 'react';
11+
12+
// Other dependencies.
13+
import { LanguageRuntimeSessionMode } from '../../../../services/languageRuntime/common/languageRuntimeService.js';
14+
import { Codicon } from '../../../../../base/common/codicons.js';
15+
import { ThemeIcon } from '../../../../../base/common/themables.js';
16+
import { positronClassNames } from '../../../../../base/common/positronUtilities.js';
17+
18+
export interface RuntimeIconProps {
19+
base64EncodedIconSvg: string | undefined;
20+
sessionMode: LanguageRuntimeSessionMode;
21+
}
22+
23+
export const RuntimeIcon = ({ base64EncodedIconSvg, sessionMode }: RuntimeIconProps) => {
24+
const classNames = ['icon']
25+
if (sessionMode === LanguageRuntimeSessionMode.Notebook) {
26+
classNames.push(...ThemeIcon.asClassNameArray(Codicon.notebook));
27+
return <span className={positronClassNames(...classNames)}></span>;
28+
}
29+
if (base64EncodedIconSvg === undefined) {
30+
return null;
31+
}
32+
return <img
33+
className={positronClassNames(...classNames)}
34+
src={`data:image/svg+xml;base64,${base64EncodedIconSvg}`}
35+
/>;
36+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
3+
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
4+
*--------------------------------------------------------------------------------------------*/
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
3+
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
// CSS.
7+
import './runtimeStatus.css';
8+
9+
// React.
10+
import React from 'react';
11+
12+
// Other dependencies.
13+
import { POSITRON_CONSOLE_STATE_ICON_ACTIVE, POSITRON_CONSOLE_STATE_ICON_DISCONNECTED, POSITRON_CONSOLE_STATE_ICON_IDLE } from '../../../../common/theme.js';
14+
import { ThemeIcon } from '../../../../../base/common/themables.js';
15+
import { Codicon } from '../../../../../base/common/codicons.js';
16+
import { registerIcon } from '../../../../../platform/theme/common/iconRegistry.js';
17+
import { localize } from '../../../../../nls.js';
18+
import { asCssVariable, ColorIdentifier } from '../../../../../platform/theme/common/colorUtils.js';
19+
20+
export const enum RuntimeStatus {
21+
Active = 'Active',
22+
Disconnected = 'Disconnected',
23+
Idle = 'Idle'
24+
}
25+
26+
const positronRuntimeStatusActiveIcon = registerIcon(
27+
'positron-runtime-status-active',
28+
Codicon.positronStatusActive,
29+
localize('positronRuntimeStatusActiveIcon', 'Icon to indicate the \'active\' status of an interpreter session.')
30+
);
31+
32+
const positronRuntimeStatusDisconnectedIcon = registerIcon(
33+
'positron-runtime-status-disconnected',
34+
Codicon.positronStatusDisconnected,
35+
localize('positronRuntimeStatusDisconnectedIcon', 'Icon to indicate the \'disconnected\' status of an interpreter session.')
36+
);
37+
38+
const positronRuntimeStatusIdleIcon = registerIcon(
39+
'positron-runtime-status-idle',
40+
Codicon.positronStatusIdle,
41+
localize('positronRuntimeStatusIdleIcon', 'Icon to indicate the \'idle\' status of an interpreter session.')
42+
);
43+
44+
const statusToIcon: Record<RuntimeStatus, ThemeIcon> = {
45+
[RuntimeStatus.Active]: positronRuntimeStatusActiveIcon,
46+
[RuntimeStatus.Disconnected]: positronRuntimeStatusDisconnectedIcon,
47+
[RuntimeStatus.Idle]: positronRuntimeStatusIdleIcon,
48+
};
49+
50+
const statusToIconColor: Record<RuntimeStatus, ColorIdentifier> = {
51+
[RuntimeStatus.Active]: POSITRON_CONSOLE_STATE_ICON_ACTIVE,
52+
[RuntimeStatus.Disconnected]: POSITRON_CONSOLE_STATE_ICON_DISCONNECTED,
53+
[RuntimeStatus.Idle]: POSITRON_CONSOLE_STATE_ICON_IDLE,
54+
};
55+
56+
export interface RuntimeStatusIconProps {
57+
status: RuntimeStatus;
58+
}
59+
60+
export const RuntimeStatusIcon = ({ status }: RuntimeStatusIconProps) => {
61+
const icon = statusToIcon[status];
62+
const className = ThemeIcon.asClassName(icon);
63+
const color = statusToIconColor[status];
64+
const colorCss = asCssVariable(color);
65+
return (
66+
<span
67+
className={className}
68+
style={{ color: colorCss }}
69+
/>
70+
);
71+
};

0 commit comments

Comments
 (0)