diff --git a/packages/base/src/panelview/formbuilder.tsx b/packages/base/src/panelview/formbuilder.tsx index dcf55c8c..201743bd 100644 --- a/packages/base/src/panelview/formbuilder.tsx +++ b/packages/base/src/panelview/formbuilder.tsx @@ -183,7 +183,7 @@ export class ObjectPropertiesForm extends React.Component { className="jpcad-property-panel" data-path={this.props.filePath ?? ''} > -
+
{formSchema}
diff --git a/packages/base/src/panelview/leftpanel.tsx b/packages/base/src/panelview/leftpanel.tsx index 4581f5cf..53b8666b 100644 --- a/packages/base/src/panelview/leftpanel.tsx +++ b/packages/base/src/panelview/leftpanel.tsx @@ -1,14 +1,15 @@ import { - IAnnotationModel, JupyterCadDoc, - IJupyterCadTracker + IJupyterCadTracker, + IJCadFormSchemaRegistry } from '@jupytercad/schema'; import { SidePanel } from '@jupyterlab/ui-components'; import { IControlPanelModel } from '../types'; -import { Annotations } from './annotations'; import { ControlPanelHeader } from './header'; import { ObjectTree } from './objecttree'; +import { ObjectProperties } from './objectproperties'; +import { AccordionPanel } from '@lumino/widgets'; export class LeftPanelWidget extends SidePanel { constructor(options: LeftPanelWidget.IOptions) { @@ -17,26 +18,27 @@ export class LeftPanelWidget extends SidePanel { this.addClass('data-jcad-keybinding'); this.node.tabIndex = 0; this._model = options.model; - this._annotationModel = options.annotationModel; const header = new ControlPanelHeader(); this.header.addWidget(header); const tree = new ObjectTree({ controlPanelModel: this._model }); this.addWidget(tree); - const annotations = new Annotations({ model: this._annotationModel }); - this.addWidget(annotations); + const properties = new ObjectProperties({ + controlPanelModel: this._model, + formSchemaRegistry: options.formSchemaRegistry, + tracker: options.tracker + }); + this.addWidget(properties); options.tracker.currentChanged.connect((_, changed) => { if (changed) { header.title.label = changed.context.localPath; - this._annotationModel.context = - options.tracker.currentWidget?.context || undefined; } else { header.title.label = '-'; - this._annotationModel.context = undefined; } }); + (this.content as AccordionPanel).setRelativeSizes([4, 6]); } dispose(): void { @@ -44,14 +46,13 @@ export class LeftPanelWidget extends SidePanel { } private _model: IControlPanelModel; - private _annotationModel: IAnnotationModel; } export namespace LeftPanelWidget { export interface IOptions { model: IControlPanelModel; - annotationModel: IAnnotationModel; tracker: IJupyterCadTracker; + formSchemaRegistry: IJCadFormSchemaRegistry; } export interface IProps { diff --git a/packages/base/src/panelview/objectproperties.tsx b/packages/base/src/panelview/objectproperties.tsx index 29d070f6..58273a4b 100644 --- a/packages/base/src/panelview/objectproperties.tsx +++ b/packages/base/src/panelview/objectproperties.tsx @@ -6,7 +6,8 @@ import { IJupyterCadClientState, IJupyterCadDoc, IJupyterCadModel, - IJupyterCadTracker + IJupyterCadTracker, + ISelection } from '@jupytercad/schema'; import { ReactWidget, showErrorMessage } from '@jupyterlab/apputils'; import { PanelWithToolbar } from '@jupyterlab/ui-components'; @@ -26,16 +27,77 @@ import { JupyterCadWidget } from '../widget'; export class ObjectProperties extends PanelWithToolbar { constructor(params: ObjectProperties.IOptions) { super(params); + const { controlPanelModel, formSchemaRegistry, tracker } = params; this.title.label = 'Objects Properties'; const body = ReactWidget.create( ); this.addWidget(body); this.addClass('jpcad-sidebar-propertiespanel'); + + const updateTitle = ( + sender: IJupyterCadModel, + clients: Map + ) => { + const localState = sender.localState; + if (!localState) { + return; + } + + let selection: { [key: string]: ISelection } = {}; + if (localState.remoteUser) { + // We are in following mode. + // Sync selections from a remote user + const remoteState = clients.get(localState.remoteUser); + + if (remoteState?.selected?.value) { + selection = remoteState?.selected?.value; + } + } else if (localState.selected?.value) { + selection = localState.selected.value; + } + const selectionNames = Object.keys(selection); + if (selectionNames.length === 1) { + const selected = selectionNames[0]; + if (selected.startsWith('edge-') && selection[selected].parent) { + this.title.label = selection[selected].parent; + } else { + this.title.label = selected; + } + } else { + this.title.label = 'No selection'; + } + }; + + let currentModel: IJupyterCadModel | undefined = undefined; + controlPanelModel.documentChanged.connect((_, changed) => { + if (changed) { + if (currentModel) { + currentModel.clientStateChanged.disconnect(updateTitle); + } + + if (changed.context.model.sharedModel.editable) { + currentModel = changed.context.model; + const clients = currentModel.sharedModel.awareness.getStates() as Map< + number, + IJupyterCadClientState + >; + updateTitle(currentModel, clients); + currentModel.clientStateChanged.connect(updateTitle); + + body.show(); + } else { + this.title.label = 'Read Only File'; + body.hide(); + } + } else { + this.title.label = '-'; + } + }); } } diff --git a/packages/base/src/panelview/objecttree.tsx b/packages/base/src/panelview/objecttree.tsx index a96abd43..bd5b3d81 100644 --- a/packages/base/src/panelview/objecttree.tsx +++ b/packages/base/src/panelview/objecttree.tsx @@ -40,7 +40,7 @@ const visibilityOffIcon = new LabIcon({ svgstr: visibilityOffSvg }); -const TREE_THEMES: ThemeSettings = { +export const TREE_THEMES: ThemeSettings = { labTheme: { text: { fontSize: '14px', @@ -345,7 +345,7 @@ class ObjectTreeReact extends React.Component { } return ( -
+
{ return (
this.handleNodeClick(opts.node.id as string)} >
- ) => { - const localState = sender.localState; - if (!localState) { - return; - } - - let selection: { [key: string]: ISelection } = {}; - if (localState.remoteUser) { - // We are in following mode. - // Sync selections from a remote user - const remoteState = clients.get(localState.remoteUser); - if (remoteState?.selected?.value) { - selection = remoteState?.selected?.value; - } - } else if (localState.selected?.value) { - selection = localState.selected.value; - } - const selectionNames = Object.keys(selection); - if (selectionNames.length === 1) { - const selected = selectionNames[0]; - if (selected.startsWith('edge-') && selection[selected].parent) { - header.title.label = selection[selected].parent; - } else { - header.title.label = selected; - } - } else { - header.title.label = 'No selection'; - } - }; + const annotations = new Annotations({ model: this._annotationModel }); + this.addWidget(annotations); - let currentModel: IJupyterCadModel | undefined = undefined; - this.addWidget(properties); - this._model.documentChanged.connect((_, changed) => { + options.tracker.currentChanged.connect((_, changed) => { if (changed) { - if (currentModel) { - currentModel.clientStateChanged.disconnect(updateTitle); - } - - if (changed.context.model.sharedModel.editable) { - currentModel = changed.context.model; - const clients = currentModel.sharedModel.awareness.getStates() as Map< - number, - IJupyterCadClientState - >; - updateTitle(currentModel, clients); - currentModel.clientStateChanged.connect(updateTitle); - - properties.show(); - } else { - header.title.label = `${changed.context.localPath} - Read Only`; - properties.hide(); - } + header.title.label = changed.context.localPath; + this._annotationModel.context = + options.tracker.currentWidget?.context || undefined; } else { header.title.label = '-'; + this._annotationModel.context = undefined; } }); } + get model(): IControlPanelModel { + return this._model; + } + dispose(): void { super.dispose(); } private _model: IControlPanelModel; + private _annotationModel: IAnnotationModel; } export namespace RightPanelWidget { export interface IOptions { model: IControlPanelModel; tracker: IJupyterCadTracker; - formSchemaRegistry: IJCadFormSchemaRegistry; + annotationModel: IAnnotationModel; } export interface IProps { filePath?: string; diff --git a/packages/base/src/suggestion/model.ts b/packages/base/src/suggestion/model.ts new file mode 100644 index 00000000..c7f90c43 --- /dev/null +++ b/packages/base/src/suggestion/model.ts @@ -0,0 +1,112 @@ +import { + IJupyterCadDoc, + IJupyterCadDocChange, + IJupyterCadTracker +} from '@jupytercad/schema'; +import { ISignal, Signal } from '@lumino/signaling'; + +export class SuggestionModel { + constructor(options: SuggestionModel.IOptions) { + this.sharedModel = options.sharedModel; + this._title = options.title; + this._tracker = options.tracker; + console.log(this._tracker); + } + + get title(): string { + return this._title ?? ''; + } + + set title(newTitle: string) { + this._title = newTitle; + } + set sharedModel(newSharedModel: IJupyterCadDoc | undefined) { + this._sharedModel?.changed.disconnect(this._onStateChanged, this); + this._sharedModel = newSharedModel; + this._sharedModel?.changed.connect(this._onStateChanged.bind(this), this); + } + get sharedModel(): IJupyterCadDoc | undefined { + return this._sharedModel; + } + + get allForks(): string[] { + return this._allForks; + } + + get forksUpdated(): ISignal { + return this._forksUpdated; + } + get forkSwitched(): ISignal { + return this._forkSwitched; + } + get contextChanged(): ISignal { + return this._contextChanged; + } + + switchContext(context: { + title: string; + sharedModel: IJupyterCadDoc | undefined; + }): void { + this._title = context.title; + this.sharedModel = context.sharedModel; + this._contextChanged.emit(); + } + + async mergeFork(forkId: string): Promise { + /** */ + } + async createFork(): Promise { + console.log('create new fork'); + const id = ''; + return id; + } + + async backToRoot(): Promise { + /** */ + } + + async checkoutFork(forkId: string, split = false): Promise { + /** */ + } + + private _onStateChanged( + sender: IJupyterCadDoc, + changes: IJupyterCadDocChange + ) { + let forkUpdated = false; + if (changes.stateChange) { + const forkPrefix = 'fork_'; + changes.stateChange.forEach(value => { + if (value.name === 'merge') { + // TODO + } else if (value.name.startsWith(forkPrefix)) { + const forkId = value.name.slice(forkPrefix.length); + if (value.newValue === 'new') { + this._allForks.push(forkId); + forkUpdated = true; + } else if (value.newValue === undefined) { + // TODO + } + } + }); + } + if (forkUpdated) { + this._forksUpdated.emit(); + } + } + private _sharedModel: IJupyterCadDoc | undefined; + private _allForks: string[] = []; + private _forksUpdated: Signal = new Signal(this); + private _forkSwitched: Signal = new Signal(this); + private _contextChanged: Signal = new Signal(this); + private _title: string | undefined; + private _tracker: IJupyterCadTracker; +} + +namespace SuggestionModel { + export interface IOptions { + sharedModel: IJupyterCadDoc | undefined; + title: string; + tracker: IJupyterCadTracker; + } +} diff --git a/packages/base/src/suggestion/suggestionpanel.tsx b/packages/base/src/suggestion/suggestionpanel.tsx new file mode 100644 index 00000000..ab2489e2 --- /dev/null +++ b/packages/base/src/suggestion/suggestionpanel.tsx @@ -0,0 +1,55 @@ +import { ReactWidget } from '@jupyterlab/apputils'; +import { + PanelWithToolbar, + ToolbarButton, + addIcon, + homeIcon +} from '@jupyterlab/ui-components'; +import { Panel } from '@lumino/widgets'; +import * as React from 'react'; +import { SuggestionModel } from './model'; +import { Suggestion } from './view'; + +export class SuggestionPanel extends PanelWithToolbar { + constructor(params: SuggestionPanel.IOptions) { + super(); + this.title.label = 'Suggestions'; + const { model } = params; + this._model = model; + const body = ReactWidget.create(); + this.addWidget(body); + this.addClass('jpcad-sidebar-propertiespanel'); + this.toolbar.addItem( + 'backToRoot', + new ToolbarButton({ + icon: homeIcon, + onClick: async () => { + await this._model.backToRoot(); + }, + tooltip: 'Return to root document' + }) + ); + this.toolbar.addItem( + 'newFork', + new ToolbarButton({ + icon: addIcon, + onClick: this.createFork.bind(this), + tooltip: 'Create new fork' + }) + ); + } + + async createFork() { + await this._model.createFork(); + } + private _model: SuggestionModel; +} + +export namespace SuggestionPanel { + /** + * Instantiation options for `ObjectProperties`. + */ + export interface IOptions extends Panel.IOptions { + model: SuggestionModel; + } +} diff --git a/packages/base/src/suggestion/view.tsx b/packages/base/src/suggestion/view.tsx new file mode 100644 index 00000000..07ebc86d --- /dev/null +++ b/packages/base/src/suggestion/view.tsx @@ -0,0 +1,118 @@ +import * as React from 'react'; +import { SuggestionModel } from './model'; +import { ReactTree, TreeNodeList } from '@naisutech/react-tree'; +import { TREE_THEMES } from '../panelview/objecttree'; +import { + ToolbarButtonComponent, + checkIcon, + closeIcon +} from '@jupyterlab/ui-components'; +import { visibilityIcon, visibilityOffIcon } from '../tools'; + +interface ISuggestionProps { + model: SuggestionModel; +} + +export const Suggestion = (props: ISuggestionProps): JSX.Element => { + const [currentForkId, setCurrentForkId] = React.useState(''); + const [forkData, setForkData] = React.useState([ + { id: 'root', label: props.model.title, parentId: null, items: [] } + ]); + + const updateFork = React.useCallback(() => { + const allForks = props.model.allForks; + const newState = [...forkData]; + newState[0] = { + ...forkData[0], + label: props.model.title, + items: allForks.map(it => ({ id: it, label: it, parentId: 'root' })) + }; + setForkData(newState); + }, [props.model, forkData]); + React.useEffect(() => { + props.model.contextChanged.connect(updateFork); + props.model.forksUpdated.connect(updateFork); + props.model.forkSwitched.connect((_, newForkId) => { + setCurrentForkId(newForkId); + }); + }, [props.model, updateFork]); + + return ( +
+
+ { + // const paddingLeft = 25 * (opts.level + 1); + return ( +
+
+ + {opts.node.label} + + {opts.type === 'leaf' ? ( +
+ { + setCurrentForkId(opts.node.id as string); + await props.model.checkoutFork( + opts.node.id as string, + true + ); + }} + icon={ + currentForkId === opts.node.id + ? visibilityIcon + : visibilityOffIcon + } + /> + { + await props.model.mergeFork(opts.node.id as string); + }} + icon={checkIcon} + /> + { + /** */ + }} + icon={closeIcon} + /> +
+ ) : null} +
+
+ ); + }} + /> +
+
+ ); +}; diff --git a/packages/base/src/tools.ts b/packages/base/src/tools.ts index 0f272e41..df676e74 100644 --- a/packages/base/src/tools.ts +++ b/packages/base/src/tools.ts @@ -21,6 +21,18 @@ import chamferIconStr from '../style/icon/chamfer.svg'; import filletIconStr from '../style/icon/fillet.svg'; import wireframeIconStr from '../style/icon/wireframe.svg'; +import visibilitySvg from '../style/icon/visibility.svg'; +import visibilityOffSvg from '../style/icon/visibilityOff.svg'; + +export const visibilityIcon = new LabIcon({ + name: 'jupytercad:visibilityIcon', + svgstr: visibilitySvg +}); +export const visibilityOffIcon = new LabIcon({ + name: 'jupytercad:visibilityOffIcon', + svgstr: visibilityOffSvg +}); + export const logoIcon = new LabIcon({ name: 'jupytercad:logo', svgstr: logoStr diff --git a/python/jupytercad_core/src/jcadplugin/plugins.ts b/python/jupytercad_core/src/jcadplugin/plugins.ts index 2cf41021..811432ed 100644 --- a/python/jupytercad_core/src/jcadplugin/plugins.ts +++ b/python/jupytercad_core/src/jcadplugin/plugins.ts @@ -122,7 +122,6 @@ const activate = ( ); tracker.add(widget); app.shell.activateById('jupytercad::leftControlPanel'); - app.shell.activateById('jupytercad::rightControlPanel'); }); app.commands.addCommand(CommandIDs.createNew, { diff --git a/python/jupytercad_lab/src/index.ts b/python/jupytercad_lab/src/index.ts index a469e712..99153ec3 100644 --- a/python/jupytercad_lab/src/index.ts +++ b/python/jupytercad_lab/src/index.ts @@ -89,11 +89,10 @@ const controlPanel: JupyterFrontEndPlugin = { formSchemaRegistry: IJCadFormSchemaRegistry ) => { const controlModel = new ControlPanelModel({ tracker }); - const leftControlPanel = new LeftPanelWidget({ model: controlModel, - annotationModel, - tracker + tracker, + formSchemaRegistry }); leftControlPanel.id = 'jupytercad::leftControlPanel'; leftControlPanel.title.caption = 'JupyterCad Control Panel'; @@ -102,7 +101,7 @@ const controlPanel: JupyterFrontEndPlugin = { const rightControlPanel = new RightPanelWidget({ model: controlModel, tracker, - formSchemaRegistry + annotationModel }); rightControlPanel.id = 'jupytercad::rightControlPanel'; rightControlPanel.title.caption = 'JupyterCad Control Panel'; @@ -113,7 +112,7 @@ const controlPanel: JupyterFrontEndPlugin = { restorer.add(rightControlPanel, NAME_SPACE); } app.shell.add(leftControlPanel, 'left', { rank: 2000 }); - app.shell.add(rightControlPanel, 'right', { rank: 2000 }); + app.shell.add(rightControlPanel, 'right', { rank: 2000, activate: false }); } }; diff --git a/ui-tests/tests/sketcher.spec.ts-snapshots/Sketcher-Circle-test-jcad-linux.png b/ui-tests/tests/sketcher.spec.ts-snapshots/Sketcher-Circle-test-jcad-linux.png index 5a477f9b..f3a15d49 100644 Binary files a/ui-tests/tests/sketcher.spec.ts-snapshots/Sketcher-Circle-test-jcad-linux.png and b/ui-tests/tests/sketcher.spec.ts-snapshots/Sketcher-Circle-test-jcad-linux.png differ diff --git a/ui-tests/tests/tree.spec.ts-snapshots/Tree-Display-test-jcad-linux.png b/ui-tests/tests/tree.spec.ts-snapshots/Tree-Display-test-jcad-linux.png index 289d69d1..274d712e 100644 Binary files a/ui-tests/tests/tree.spec.ts-snapshots/Tree-Display-test-jcad-linux.png and b/ui-tests/tests/tree.spec.ts-snapshots/Tree-Display-test-jcad-linux.png differ diff --git a/ui-tests/tests/ui.spec.ts b/ui-tests/tests/ui.spec.ts index 96a4a964..f40ed83d 100644 --- a/ui-tests/tests/ui.spec.ts +++ b/ui-tests/tests/ui.spec.ts @@ -59,17 +59,13 @@ test.describe('UI Test', () => { await page.notebook.activate(fullPath); await page.locator('div.jpcad-Spinner').waitFor({ state: 'hidden' }); await page.waitForTimeout(1000); + if (await page.getByRole('button', { name: 'Ok' }).isVisible()) { await page.getByRole('button', { name: 'Ok' }).click(); } - await page - .getByRole('tablist', { name: 'main sidebar' }) - .getByRole('tab', { name: 'JupyterCad Control Panel' }) - .click(); - await page - .getByRole('tablist', { name: 'alternate sidebar' }) - .getByRole('tab', { name: 'JupyterCad Control Panel' }) - .click(); + + await page.sidebar.close('left'); + await page.sidebar.close('right'); await page.waitForTimeout(1000); const main = await page.$('#jp-main-split-panel'); expect(errors).toBe(0); @@ -128,14 +124,8 @@ test.describe('UI Test', () => { }) .click(); - await page - .getByRole('tablist', { name: 'main sidebar' }) - .getByRole('tab', { name: 'JupyterCad Control Panel' }) - .click(); - await page - .getByRole('tablist', { name: 'alternate sidebar' }) - .getByRole('tab', { name: 'JupyterCad Control Panel' }) - .click(); + await page.sidebar.close('left'); + await page.sidebar.close('right'); await page.waitForTimeout(1000); expect(errors).toBe(0); @@ -170,14 +160,8 @@ test.describe('UI Test', () => { await page.getByRole('button', { name: 'Ok' }).click(); } - await page - .getByRole('tablist', { name: 'main sidebar' }) - .getByRole('tab', { name: 'JupyterCad Control Panel' }) - .click(); - await page - .getByRole('tablist', { name: 'alternate sidebar' }) - .getByRole('tab', { name: 'JupyterCad Control Panel' }) - .click(); + await page.sidebar.close('left'); + await page.sidebar.close('right'); await page.waitForTimeout(1000); expect(errors).toBe(0); @@ -214,14 +198,8 @@ test.describe('UI Test', () => { .click(); // Hide side bars for the screenshot - await page - .getByRole('tablist', { name: 'main sidebar' }) - .getByRole('tab', { name: 'JupyterCad Control Panel' }) - .click(); - await page - .getByRole('tablist', { name: 'alternate sidebar' }) - .getByRole('tab', { name: 'JupyterCad Control Panel' }) - .click(); + await page.sidebar.close('left'); + await page.sidebar.close('right'); await page.waitForTimeout(1000); expect(errors).toBe(0); @@ -242,7 +220,7 @@ test.describe('UI Test', () => { await page.notebook.openByPath(fullPath); await page.notebook.activate(fullPath); await page.locator('div.jpcad-Spinner').waitFor({ state: 'hidden' }); - + await page.sidebar.close('right'); // Create a cone await page.getByTitle('New Cone').click(); await page.getByLabel('Radius1').click(); @@ -316,6 +294,8 @@ test.describe('UI Test', () => { name: `JCAD-New.png` }); } + await page.sidebar.open('left'); + await page.waitForTimeout(500); await page.getByLabel('Length*').fill('0.5'); await page.getByRole('button', { name: 'Submit' }).click(); await page.waitForTimeout(500); @@ -324,6 +304,7 @@ test.describe('UI Test', () => { await page.waitForTimeout(500); await page.getByLabel('Height*').fill('2'); await page.getByRole('button', { name: 'Submit' }).click(); + await page.sidebar.close('left'); await page.waitForTimeout(500); if (main) { expect(await main.screenshot()).toMatchSnapshot({ @@ -356,8 +337,9 @@ test.describe('UI Test', () => { test.describe('Console activation test', () => { test('should open console', async ({ page }) => { await page.goto(); + await page.sidebar.close('right'); + await page.sidebar.close('left'); await page.getByLabel('notebook content').getByText('CAD File').click(); - await page.getByRole('button', { name: 'More commands' }).click(); await page.getByRole('button', { name: 'Toggle console' }).click(); await page.getByRole('button', { name: 'Remove console' }); await page.getByRole('textbox').nth(1).click(); diff --git a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Console-linux.png b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Console-linux.png index c1a245d7..4b8e65f0 100644 Binary files a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Console-linux.png and b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Console-linux.png differ diff --git a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Modified-linux.png b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Modified-linux.png index f23cb752..991d2ad0 100644 Binary files a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Modified-linux.png and b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Modified-linux.png differ diff --git a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-New-linux.png b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-New-linux.png index a2ce0fc8..3f9e56e9 100644 Binary files a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-New-linux.png and b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-New-linux.png differ diff --git a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Redo-linux.png b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Redo-linux.png index ca2984a8..f174fef7 100644 Binary files a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Redo-linux.png and b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Redo-linux.png differ diff --git a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Undo-linux.png b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Undo-linux.png index 3fcf7d46..369603c5 100644 Binary files a/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Undo-linux.png and b/ui-tests/tests/ui.spec.ts-snapshots/JCAD-Undo-linux.png differ diff --git a/ui-tests/tests/ui.spec.ts-snapshots/MultiSelect-Cut-test-jcad-linux.png b/ui-tests/tests/ui.spec.ts-snapshots/MultiSelect-Cut-test-jcad-linux.png index 60a9cf11..1711e621 100644 Binary files a/ui-tests/tests/ui.spec.ts-snapshots/MultiSelect-Cut-test-jcad-linux.png and b/ui-tests/tests/ui.spec.ts-snapshots/MultiSelect-Cut-test-jcad-linux.png differ diff --git a/ui-tests/tests/ui.spec.ts-snapshots/MultiSelect-test-jcad-linux.png b/ui-tests/tests/ui.spec.ts-snapshots/MultiSelect-test-jcad-linux.png index 5f76ef9c..a9582e19 100644 Binary files a/ui-tests/tests/ui.spec.ts-snapshots/MultiSelect-test-jcad-linux.png and b/ui-tests/tests/ui.spec.ts-snapshots/MultiSelect-test-jcad-linux.png differ