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

✨Enable undo and redo #110

Merged
merged 9 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
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
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.