Skip to content

Commit

Permalink
Merge pull request #110 from XpressAI/adry/undo-redo
Browse files Browse the repository at this point in the history
✨Enable undo and redo
  • Loading branch information
MFA-X-AI authored Mar 1, 2022
2 parents 7554dcf + d7b2f19 commit 890ae33
Show file tree
Hide file tree
Showing 15 changed files with 122 additions and 42 deletions.
20 changes: 15 additions & 5 deletions schema/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,40 @@
"jupyter.lab.menus": {
"context": [
{
"command": "Xircuit-editor:cut-node",
"command": "Xircuit-editor:undo",
"selector": ".xircuits-editor",
"rank": 0
},
{
"command": "Xircuit-editor:copy-node",
"command": "Xircuit-editor:redo",
"selector": ".xircuits-editor",
"rank": 1
},
{
"command": "Xircuit-editor:paste-node",
"command": "Xircuit-editor:cut-node",
"selector": ".xircuits-editor",
"rank": 2
},
{
"command": "Xircuit-editor:edit-node",
"command": "Xircuit-editor:copy-node",
"selector": ".xircuits-editor",
"rank": 3
},
{
"command": "Xircuit-editor:delete-node",
"command": "Xircuit-editor:paste-node",
"selector": ".xircuits-editor",
"rank": 4
},
{
"command": "Xircuit-editor:edit-node",
"selector": ".xircuits-editor",
"rank": 5
},
{
"command": "Xircuit-editor:delete-node",
"selector": ".xircuits-editor",
"rank": 6
},
{
"type": "separator",
"selector": ".xircuits-editor",
Expand Down
40 changes: 40 additions & 0 deletions src/commands/ContextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { XPipePanel } from '../xircuitWidget';
import { Dialog, showDialog } from '@jupyterlab/apputils';
import { DefaultLinkModel } from '@projectstorm/react-diagrams';
import { BaseModel, BaseModelGenerics } from '@projectstorm/react-canvas-core';
import { copyIcon, cutIcon, pasteIcon, redoIcon, undoIcon } from '@jupyterlab/ui-components';

/**
* Add the commands for the xircuits's context menu.
Expand All @@ -30,10 +31,47 @@ export function addContextMenuCommands(
);
}

//Add command to undo
commands.addCommand(commandIDs.undo, {
execute: () =>{
const widget = tracker.currentWidget?.content as XPipePanel;
const model = widget.context.model.sharedModel;

model.undo();
},
label: trans.__('Undo'),
icon: undoIcon,
isEnabled: () => {
const widget = tracker.currentWidget?.content as XPipePanel;
const canUndo = widget.context.model.sharedModel.canUndo();

return canUndo ?? false;
}
});

//Add command to redo
commands.addCommand(commandIDs.redo, {
execute: () => {
const widget = tracker.currentWidget?.content as XPipePanel;
const model = widget.context.model.sharedModel;

model.redo();
},
label: trans.__('Redo'),
icon: redoIcon,
isEnabled: () => {
const widget = tracker.currentWidget?.content as XPipePanel;
const canRedo = widget.context.model.sharedModel.canRedo();

return canRedo ?? false;
}
});

//Add command to cut node
commands.addCommand(commandIDs.cutNode, {
execute: cutNode,
label: trans.__('Cut'),
icon: cutIcon,
isEnabled: () => {
const widget = tracker.currentWidget?.content as XPipePanel;
const selectedEntities = widget.xircuitsApp.getDiagramEngine().getModel().getSelectedEntities();
Expand All @@ -49,6 +87,7 @@ export function addContextMenuCommands(
commands.addCommand(commandIDs.copyNode, {
execute: copyNode,
label: trans.__('Copy'),
icon: copyIcon,
isEnabled: () => {
const widget = tracker.currentWidget?.content as XPipePanel;
const selectedEntities = widget.xircuitsApp.getDiagramEngine().getModel().getSelectedEntities();
Expand All @@ -64,6 +103,7 @@ export function addContextMenuCommands(
commands.addCommand(commandIDs.pasteNode, {
execute: pasteNode,
label: trans.__('Paste'),
icon: pasteIcon,
isEnabled: () => {
const clipboard = JSON.parse(localStorage.getItem('clipboard'));
let isClipboardFilled: boolean
Expand Down
5 changes: 4 additions & 1 deletion src/commands/CustomActionEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export class CustomActionEvent extends Action {
const app = options.app;
const keyCode = event.event.key;
const ctrlKey = event.event.ctrlKey;


if (ctrlKey && keyCode === 'z') app.commands.execute(commandIDs.undo);
if (ctrlKey && keyCode === 'y') app.commands.execute(commandIDs.redo);
if (ctrlKey && keyCode === 's') app.commands.execute(commandIDs.saveXircuit);
// Comment this first until the TODO below is fix
// if (ctrlKey && keyCode === 'x') app.commands.execute(commandIDs.cutNode);
// if (ctrlKey && keyCode === 'c') app.commands.execute(commandIDs.copyNode);
Expand Down
44 changes: 37 additions & 7 deletions src/components/xircuitBodyWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
import { LinkModel, DiagramModel, DiagramEngine, DefaultLinkModel } from '@projectstorm/react-diagrams';
import { NodeModel } from "@projectstorm/react-diagrams-core/src/entities/node/NodeModel";
import { Dialog, showDialog } from '@jupyterlab/apputils';
import { Dialog, showDialog, showErrorMessage } from '@jupyterlab/apputils';
import { ILabShell, JupyterFrontEnd } from '@jupyterlab/application';
import { Signal } from '@lumino/signaling';
import {
Expand Down Expand Up @@ -91,6 +91,8 @@ export const commandIDs = {
runXircuit: 'Xircuit-editor:run-node',
debugXircuit: 'Xircuit-editor:debug-node',
lockXircuit: 'Xircuit-editor:lock-node',
undo: 'Xircuit-editor:undo',
redo: 'Xircuit-editor:redo',
cutNode: 'Xircuit-editor:cut-node',
copyNode: 'Xircuit-editor:copy-node',
pasteNode: 'Xircuit-editor:paste-node',
Expand Down Expand Up @@ -168,6 +170,7 @@ export const BodyWidget: FC<BodyWidgetProps> = ({
const [runType, setRunType] = useState<string>("run");
const xircuitLogger = new Log(app);
const contextRef = useRef(context);
const notInitialRender = useRef(false);

const onChange = useCallback(
(): void => {
Expand All @@ -182,6 +185,11 @@ export const BodyWidget: FC<BodyWidgetProps> = ({

const customDeserializeModel = (modelContext: any, diagramEngine: DiagramEngine) => {

if (modelContext == null) {
// When context empty, just return
return;
}

let tempModel = new DiagramModel();
let links = modelContext["layers"][0]["models"];
let nodes = modelContext["layers"][1]["models"];
Expand Down Expand Up @@ -274,12 +282,26 @@ export const BodyWidget: FC<BodyWidgetProps> = ({

useEffect(() => {
const currentContext = contextRef.current;

const changeHandler = (): void => {
const model: any = currentContext.model.toJSON();
if (context.isReady) {
let deserializedModel = customDeserializeModel(model, xircuitsApp.getDiagramEngine());
xircuitsApp.getDiagramEngine().setModel(deserializedModel);
const modelStr = currentContext.model.toString();
if (!isJSON(modelStr)) {
// When context can't be parsed, just return
return
}

try {
if (notInitialRender.current) {
const model: any = currentContext.model.toJSON();
let deserializedModel = customDeserializeModel(model, xircuitsApp.getDiagramEngine());
xircuitsApp.getDiagramEngine().setModel(deserializedModel);
} else {
// Clear undo history when first time rendering
notInitialRender.current = true;
currentContext.model.sharedModel.clearUndoHistory();
}
} catch (e) {
showErrorMessage('Error', <pre>{e}</pre>)
}
};

Expand All @@ -291,6 +313,14 @@ export const BodyWidget: FC<BodyWidgetProps> = ({
};
}, []);

const isJSON = (str) => {
try {
return (JSON.parse(str) && !!str);
} catch (e) {
return false;
}
}

const getBindingIndexById = (nodeModels: any[], id: string): number | null => {
for (let i = 0; i < nodeModels.length; i++) {
let nodeModel = nodeModels[i];
Expand Down Expand Up @@ -742,7 +772,7 @@ export const BodyWidget: FC<BodyWidgetProps> = ({
if (shell.currentWidget?.id !== widgetId) {
return;
}
onChange();
onChange()
setInitialize(true);
setSaved(true);
commands.execute(commandIDs.saveDocManager);
Expand Down
4 changes: 2 additions & 2 deletions src/debugger/SidebarDebugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { commandIDs } from '../components/xircuitBodyWidget';
import { DebuggerWidget } from './DebuggerWidget';
import { XircuitFactory } from '../xircuitFactory';
import { Toolbar, CommandToolbarButton } from '@jupyterlab/apputils';
import { breakpointIcon } from '../ui-components/icons';
import { breakpointIcon, nextIcon } from '../ui-components/icons';

export const DebuggerCommandIDs = {
continue: 'Xircuits-debugger:continue',
Expand Down Expand Up @@ -146,7 +146,7 @@ export const DebuggerCommandIDs = {
// Add command signal to toggle next node
app.commands.addCommand(commandIDs.nextNode, {
caption: trans.__('Next Node'),
iconClass: 'jp-NextLogo',
icon: nextIcon,
isEnabled: () => {
return inDebugMode ?? false;
},
Expand Down
4 changes: 2 additions & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { DocumentWidget } from '@jupyterlab/docregistry';
import { runIcon, saveIcon } from '@jupyterlab/ui-components';
import { addContextMenuCommands } from './commands/ContextMenu';
import { Token } from '@lumino/coreutils';
import { xircuitsIcon, debuggerIcon, changeFavicon, xircuitsFaviconLink } from './ui-components/icons';
import { xircuitsIcon, debuggerIcon, componentLibIcon, changeFavicon, xircuitsFaviconLink } from './ui-components/icons';


const FACTORY = 'Xircuits editor';
Expand Down Expand Up @@ -141,7 +141,7 @@ const xircuits: JupyterFrontEndPlugin<void> = {
// Creating the sidebar widget for the xai components
const sidebarWidget = ReactWidget.create(<Sidebar lab={app}/>);
sidebarWidget.id = 'xircuits-component-sidebar';
sidebarWidget.title.iconClass = 'jp-ComponentLibraryLogo';
sidebarWidget.title.icon = componentLibIcon;
sidebarWidget.title.caption = "Xircuits Component Library";

restorer.add(sidebarWidget, sidebarWidget.id);
Expand Down
6 changes: 6 additions & 0 deletions src/ui-components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import xircuitsSvg from '../../style/icons/xpress-logo.svg';
import debuggerSvg from '../../style/icons/debugger.svg';
import lockSvg from '../../style/icons/lock.svg';
import breakpointSvg from '../../style/icons/breakpoint.svg';
import nextSvg from '../../style/icons/next.svg';
import revertSvg from '../../style/icons/revert.svg';
import componentLibSvg from '../../style/icons/component-library.svg';

export const xircuitsFaviconLink = 'https://raw.githubusercontent.com/XpressAI/xircuits/master/style/icons/xpress-logo.ico';
export const xircuitsIcon = new LabIcon({ name: 'xircuits:xircuits', svgstr: xircuitsSvg });
export const debuggerIcon = new LabIcon({ name: 'xircuits:debuggerIcon', svgstr: debuggerSvg });
export const lockIcon = new LabIcon({ name: 'xircuits:lockIcon', svgstr: lockSvg });
export const breakpointIcon = new LabIcon({ name: 'xircuits:breakpointIcon', svgstr: breakpointSvg });
export const nextIcon = new LabIcon({ name: 'xircuits:nextIcon', svgstr: nextSvg });
export const revertIcon = new LabIcon({ name: 'xircuits:revertIcon', svgstr: revertSvg });
export const componentLibIcon = new LabIcon({ name: 'xircuits:componentLibIcon', svgstr: componentLibSvg });

export function changeFavicon(src: string) {
let head = document.head || document.getElementsByTagName('head')[0];
Expand Down
7 changes: 3 additions & 4 deletions src/xircuitFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ import {
listIcon,
refreshIcon,
runIcon,
saveIcon,
undoIcon
saveIcon
} from '@jupyterlab/ui-components';
import { ToolbarButton } from '@jupyterlab/apputils';
import { commandIDs } from './components/xircuitBodyWidget';
import { CommandIDs } from './log/LogPlugin';
import { ServiceManager } from '@jupyterlab/services';
import { RunSwitcher } from './components/RunSwitcher';
import { lockIcon, xircuitsIcon } from './ui-components/icons';
import { lockIcon, revertIcon, xircuitsIcon } from './ui-components/icons';

const XPIPE_CLASS = 'xircuits-editor';

Expand Down Expand Up @@ -136,7 +135,7 @@ export class XircuitFactory extends ABCWidgetFactory<DocumentWidget> {
* Create a revert button toolbar item.
*/
let revertButton = new ToolbarButton({
icon: undoIcon,
icon: revertIcon,
tooltip: 'Revert Xircuits to Checkpoint',
onClick: (): void => {
this.commands.execute(commandIDs.revertDocManager);
Expand Down
12 changes: 0 additions & 12 deletions style/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,6 @@
flex: 0 0 auto;
}

.jp-ComponentLibraryLogo {
background-image: url(./icons/component-library.svg);
background-repeat: no-repeat;
background-size: 28px 28px;
}

.jp-NextLogo {
background-image: url(./icons/next.svg);
background-repeat: no-repeat;
background-size: 25px 25px;
}

/* Close button for image viewer*/
.close {
color: #fff;
Expand Down
6 changes: 3 additions & 3 deletions style/icons/breakpoint.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions style/icons/component-library.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion style/icons/debugger.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion style/icons/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion style/icons/next.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions style/icons/revert.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 890ae33

Please sign in to comment.