Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add python console to 3D view #392

Merged
merged 7 commits into from
Aug 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ repos:
rev: v4.0.0-alpha.8
hooks:
- id: prettier
entry: prettier --no-error-on-unmatched-pattern --write --ignore-unknown

- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.4.0
2 changes: 2 additions & 0 deletions packages/base/package.json
Original file line number Diff line number Diff line change
@@ -42,6 +42,8 @@
"@jupytercad/schema": "^2.0.2",
"@jupyterlab/application": "^4.0.0",
"@jupyterlab/apputils": "^4.0.0",
"@jupyterlab/completer": "^4.2.4",
"@jupyterlab/console": "^4.2.4",
"@jupyterlab/coreutils": "^6.0.0",
"@jupyterlab/docregistry": "^4.0.0",
"@jupyterlab/filebrowser": "^4.0.0",
1 change: 1 addition & 0 deletions packages/base/src/3dview/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './mainview';
export * from './mainviewwidget';
24 changes: 24 additions & 0 deletions packages/base/src/3dview/mainviewwidget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ReactWidget } from '@jupyterlab/apputils';
import * as React from 'react';

import { MainView } from './mainview';
import { MainViewModel } from './mainviewmodel';

export class JupyterCadMainViewPanel extends ReactWidget {
/**
* Construct a `JupyterCadPanel`.
*
* @param context - The documents context.
*/
constructor(options: { mainViewModel: MainViewModel }) {
super();
this._mainViewModel = options.mainViewModel;
this.addClass('jp-jupytercad-panel');
}

render(): JSX.Element {
return <MainView viewModel={this._mainViewModel} />;
}

private _mainViewModel: MainViewModel;
}
107 changes: 105 additions & 2 deletions packages/base/src/commands.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ import { JupyterFrontEnd } from '@jupyterlab/application';
import { showErrorMessage, WidgetTracker } from '@jupyterlab/apputils';
import { ITranslator } from '@jupyterlab/translation';
import { redoIcon, undoIcon } from '@jupyterlab/ui-components';

import { ICompletionProviderManager } from '@jupyterlab/completer';
import { FormDialog } from './formdialog';
import { SketcherDialog } from './sketcher/sketcherdialog';
import {
@@ -696,12 +696,69 @@ export function addCommands(
tracker: WidgetTracker<JupyterCadWidget>,
translator: ITranslator,
formSchemaRegistry: IJCadFormSchemaRegistry,
workerRegistry: IJCadWorkerRegistry
workerRegistry: IJCadWorkerRegistry,
completionProviderManager: ICompletionProviderManager | undefined
): void {
workerRegistry.getWorker;
const trans = translator.load('jupyterlab');
const { commands } = app;
Private.updateFormSchema(formSchemaRegistry);

commands.addCommand(CommandIDs.toggleConsole, {
label: trans.__('Toggle console'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
execute: async () => await Private.toggleConsole(tracker)
});
commands.addCommand(CommandIDs.executeConsole, {
label: trans.__('Execute console'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
execute: () => Private.executeConsole(tracker)
});
commands.addCommand(CommandIDs.removeConsole, {
label: trans.__('Remove console'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
execute: () => Private.removeConsole(tracker)
});

commands.addCommand(CommandIDs.invokeCompleter, {
label: trans.__('Display the completion helper.'),
execute: () => {
const currentWidget = tracker.currentWidget;
if (!currentWidget || !completionProviderManager) {
return;
}
const id = currentWidget.content.consolePanel?.id;
if (id) {
return completionProviderManager.invoke(id);
}
}
});

commands.addCommand(CommandIDs.selectCompleter, {
label: trans.__('Select the completion suggestion.'),
execute: () => {
const currentWidget = tracker.currentWidget;
if (!currentWidget || !completionProviderManager) {
return;
}
const id = currentWidget.content.consolePanel?.id;
if (id) {
return completionProviderManager.select(id);
}
}
});
commands.addCommand(CommandIDs.redo, {
label: trans.__('Redo'),
isEnabled: () => {
@@ -1062,6 +1119,12 @@ export namespace CommandIDs {
export const updateClipView = 'jupytercad:updateClipView';

export const exportJcad = 'jupytercad:exportJcad';

export const toggleConsole = 'jupytercad:toggleConsole';
export const invokeCompleter = 'jupytercad:invokeConsoleCompleter';
export const removeConsole = 'jupytercad:removeConsole';
export const executeConsole = 'jupytercad:executeConsole';
export const selectCompleter = 'jupytercad:selectConsoleCompleter';
}

namespace Private {
@@ -1217,4 +1280,44 @@ namespace Private {
await dialog.launch();
};
}

export function executeConsole(
tracker: WidgetTracker<JupyterCadWidget>
): void {
const current = tracker.currentWidget;

if (!current) {
return;
}
current.content.executeConsole();
}

export function removeConsole(
tracker: WidgetTracker<JupyterCadWidget>
): void {
const current = tracker.currentWidget;

if (!current) {
return;
}
current.content.removeConsole();
}

export async function toggleConsole(
tracker: WidgetTracker<JupyterCadWidget>
): Promise<void> {
const current = tracker.currentWidget;

if (!current) {
return;
}
const currentPath = current.context.path.split(':');
let realPath = '';
if (currentPath.length > 1) {
realPath = currentPath[1];
} else {
realPath = currentPath[0];
}
await current.content.toggleConsole(realPath);
}
}
87 changes: 87 additions & 0 deletions packages/base/src/console/consoleview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { ConsolePanel } from '@jupyterlab/console';
import { ServiceManager } from '@jupyterlab/services';
import { BoxPanel, Widget } from '@lumino/widgets';
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
import { debounce } from '../tools';
import {
closeIcon,
CommandToolbarButton,
expandIcon,
Toolbar
} from '@jupyterlab/ui-components';
import { CommandRegistry } from '@lumino/commands';

export class ConsoleView extends BoxPanel {
constructor(options: ConsoleView.IOptions) {
super({ direction: 'top-to-bottom' });
this.addClass('jpcad-console');
const { manager, contentFactory, mimeTypeService, rendermime } = options;
const clonedRendermime = rendermime.clone();
this._consolePanel = new ConsolePanel({
manager,
contentFactory,
mimeTypeService,
rendermime: clonedRendermime,
kernelPreference: { name: 'python3', shutdownOnDispose: true }
});
this._consolePanel.console.node.dataset.jpInteractionMode = 'notebook';
this.addWidget(this._consolePanel);
BoxPanel.setStretch(this._consolePanel, 1);

this._consolePanel.toolbar.addItem('spacer', Toolbar.createSpacerItem());

this._consolePanel.toolbar.addItem(
'toggle',
new CommandToolbarButton({
label: '',
icon: expandIcon,
id: 'jupytercad:toggleConsole',
commands: options.commandRegistry
})
);
this._consolePanel.toolbar.addItem(
'close',
new CommandToolbarButton({
label: '',
icon: closeIcon,
id: 'jupytercad:removeConsole',
commands: options.commandRegistry
})
);
}

get consolePanel() {
return this._consolePanel;
}

dispose(): void {
if (this.isDisposed) {
return;
}
this._consolePanel.dispose();
super.dispose();
}
execute() {
this._consolePanel.console.execute(false);
}

protected onResize(msg: Widget.ResizeMessage): void {
super.onResize(msg);
this._resize();
}
private _consolePanel: ConsolePanel;
private _resize = debounce(() => {
window.dispatchEvent(new Event('resize'));
}, 200);
}

export namespace ConsoleView {
export interface IOptions {
manager: ServiceManager.IManager;
contentFactory: ConsolePanel.IContentFactory;
mimeTypeService: IEditorMimeTypeService;
rendermime: IRenderMimeRegistry;
commandRegistry: CommandRegistry;
}
}
1 change: 1 addition & 0 deletions packages/base/src/console/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './consoleview';
15 changes: 15 additions & 0 deletions packages/base/src/keybindings.json
Original file line number Diff line number Diff line change
@@ -13,5 +13,20 @@
"command": "jupytercad:removeObject",
"keys": ["Delete"],
"selector": "body"
},
{
"command": "jupytercad:executeConsole",
"keys": ["Shift Enter"],
"selector": ".jpcad-console .jp-CodeConsole[data-jp-interaction-mode='notebook'] .jp-CodeConsole-promptCell"
},
{
"command": "jupytercad:invokeConsoleCompleter",
"keys": ["Tab"],
"selector": ".jpcad-console .jp-CodeConsole-promptCell .jp-mod-completer-enabled"
},
{
"command": "jupytercad:selectConsoleCompleter",
"keys": ["Enter"],
"selector": ".jpcad-console .jp-ConsolePanel .jp-mod-completer-active"
}
]
13 changes: 12 additions & 1 deletion packages/base/src/toolbar/usertoolbaritem.tsx
Original file line number Diff line number Diff line change
@@ -71,10 +71,21 @@ export class UsersItem extends React.Component<IProps, IState> {
return el;
}

private filterDuplicated(usersList: IUserData[]): IUserData[] {
const newList: IUserData[] = [];
const selected = new Set<string>();
for (const element of usersList) {
if (!selected.has(element.userData.username)) {
selected.add(element.userData.username);
newList.push(element);
}
}
return newList;
}
render(): React.ReactNode {
return (
<div className="jpcad-toolbar-usertoolbar">
{this.state.usersList.map(item => {
{this.filterDuplicated(this.state.usersList).map(item => {
Copy link
Member

Choose a reason for hiding this comment

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

Nice! I've seen this issue once indeed when using the Python API next to the main view

if (item.userId !== this._model.currentUserId) {
return this.createUserIcon(item);
}
13 changes: 12 additions & 1 deletion packages/base/src/toolbar/widget.tsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,8 @@ import {
ReactWidget,
redoIcon,
Toolbar,
undoIcon
undoIcon,
terminalIcon
} from '@jupyterlab/ui-components';
import { CommandRegistry } from '@lumino/commands';
import { Widget } from '@lumino/widgets';
@@ -200,7 +201,17 @@ export class ToolbarWidget extends Toolbar {
);

this.addItem('separator6', new Separator());
this.addItem(
'Toggle console',
new CommandToolbarButton({
id: CommandIDs.toggleConsole,
commands: options.commands,
label: '',
icon: terminalIcon
})
);

this.addItem('separator7', new Separator());
(options.externalCommands ?? []).forEach(cmd => {
this.addItem(
cmd.name,
Loading
Loading