Skip to content

Commit

Permalink
Basic liveshare support.
Browse files Browse the repository at this point in the history
  • Loading branch information
hediet committed Aug 27, 2020
1 parent 8605364 commit 6376917
Show file tree
Hide file tree
Showing 16 changed files with 1,845 additions and 64 deletions.
26 changes: 25 additions & 1 deletion .vscode/launch.json
@@ -1,13 +1,20 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Drawio To Debug Plugins",
"request": "launch",
"type": "pwa-chrome",
"url": "https://app.diagrams.net/",
"webRoot": "${workspaceFolder}/drawio-custom-plugins/src"
},
{
"name": "Extension (dev)",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--disable-extensions",
"--disable-extension=hediet.vscode-drawio-insiders-build",
"--extensionDevelopmentPath=${workspaceFolder}",
"--enable-proposed-api hediet.vscode-drawio",
"${workspaceFolder}/examples"
Expand All @@ -18,6 +25,23 @@
"DEV": "1"
}
},
{
"name": "Extension (dev) 2",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--disable-extension=hediet.vscode-drawio-insiders-build",
"--extensionDevelopmentPath=${workspaceFolder}",
"--enable-proposed-api hediet.vscode-drawio",
"${workspaceFolder}/docs"
],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "npm: dev",
"env": {
"DEV": "1"
}
},
{
"name": "Extension",
"type": "extensionHost",
Expand Down
78 changes: 78 additions & 0 deletions drawio-custom-plugins/src/drawio-types.d.ts
@@ -0,0 +1,78 @@
declare const Draw: {
loadPlugin(handler: (ui: DrawioUI) => void): void;
};

declare const log: any;
declare class mxCellHighlight {
constructor(graph: DrawioGraph, color: string, arg: number);

public highlight(arg: DrawioCellState | null): void;
public destroy(): void;
}

declare class mxMouseEvent {
public readonly graphX: number;
public readonly graphY: number;
}

declare const mxEvent: {
DOUBLE_CLICK: string;
CHANGE: string;
};

declare const mxUtils: {
isNode(node: any): node is HTMLElement;
createXmlDocument(): XMLDocument;
};


declare interface DrawioUI {
editor: DrawioEditor;
}

declare interface DrawioEditor {
graph: DrawioGraph;
}

declare interface DrawioGraph {
insertVertex(arg0: undefined, arg1: null, label: string, arg3: number, arg4: number, arg5: number, arg6: number, arg7: string): void;
addListener: any;
model: DrawioGraphModel;
getLabel(cell: DrawioCell): string;
getSelectionModel(): DrawioGraphSelectionModel;
view: DrawioGraphView;

addMouseListener(listener: {
mouseMove?: (graph: DrawioGraph, event: mxMouseEvent) => void;
mouseDown?: (graph: DrawioGraph, event: mxMouseEvent) => void
mouseUp?: (graph: DrawioGraph, event: mxMouseEvent) => void;
}): void;
}

declare interface DrawioGraphView {
getState(cell: DrawioCell): DrawioCellState;
canvas: SVGElement;
}

declare interface DrawioCellState {
cell: DrawioCell;
}

declare interface DrawioGraphSelectionModel {
addListener(event: string, handler: (...args: any[]) => void): void;
cells: DrawioCell[];
}

declare interface DrawioCell {
id: string;
style: string
}

declare interface DrawioGraphModel {
setValue(c: DrawioCell, label: string): void;
beginUpdate(): void;
endUpdate(): void;
cells: Record<any, DrawioCell>;
setStyle(cell: DrawioCell, style: string): void;
isVertex(cell: DrawioCell): boolean;
}
3 changes: 2 additions & 1 deletion drawio-custom-plugins/src/index.ts
@@ -1 +1,2 @@
require("./linkSelectedNodeWithData");
import "./linkSelectedNodeWithData";
import "./liveshare";
28 changes: 8 additions & 20 deletions drawio-custom-plugins/src/linkSelectedNodeWithData.ts
Expand Up @@ -3,29 +3,17 @@ import {
FlattenToDictionary,
JSONValue,
} from "@hediet/json-to-dictionary";
import { sendEvent } from "./vscode";

declare const Draw: any;
declare const log: any;
declare const mxCellHighlight: any;
declare const mxEvent: any;
declare const mxUtils: {
isNode(node: any): node is HTMLElement;
createXmlDocument(): XMLDocument;
};

function sendEvent(data: CustomDrawioEvent) {
window.opener.postMessage(JSON.stringify(data), "*");
}

Draw.loadPlugin(function (ui: any) {
sendEvent({ event: "pluginLoaded" });
Draw.loadPlugin((ui) => {
sendEvent({ event: "pluginLoaded", pluginId: "linkSelectedNodeWithData" });

let interceptNodeClick = false;
const graph = ui.editor.graph;
const highlight = new mxCellHighlight(graph, "#00ff00", 8);

const model: { setStyle(cell: unknown, style: string): void } = graph.model;
let activeCell: { style: string } | undefined = undefined;
const model = graph.model;
let activeCell: DrawioCell | undefined = undefined;

graph.addListener(mxEvent.DOUBLE_CLICK, function (sender: any, evt: any) {
if (!interceptNodeClick) {
Expand All @@ -35,7 +23,7 @@ Draw.loadPlugin(function (ui: any) {
var cell: any | null = evt.getProperty("cell");
if (cell != null) {
const label = getLabelTextOfCell(cell);
if (!label.match(/^#([a-zA-Z0-9_]+)/)) {
if (!label.match(/#([a-zA-Z0-9_]+)/)) {
return;
}

Expand All @@ -46,7 +34,7 @@ Draw.loadPlugin(function (ui: any) {
});

function getLabelTextOfCell(cell: any): string {
const labelHtml = graph.getLabel(cell) as string;
const labelHtml = graph.getLabel(cell);
const el = document.createElement("html");
el.innerHTML = labelHtml; // label can be html
return el.innerText;
Expand All @@ -55,7 +43,7 @@ Draw.loadPlugin(function (ui: any) {
const selectionModel = graph.getSelectionModel();
selectionModel.addListener(mxEvent.CHANGE, (sender: any, evt: any) => {
// selection has changed
const cells: any[] = selectionModel.cells; // array of cells
const cells = selectionModel.cells;
if (cells.length >= 1) {
const selectedCell = cells[0];
activeCell = selectedCell;
Expand Down
141 changes: 141 additions & 0 deletions drawio-custom-plugins/src/liveshare.ts
@@ -0,0 +1,141 @@
import { sendEvent } from "./vscode";

Draw.loadPlugin((ui) => {
sendEvent({ event: "pluginLoaded", pluginId: "LiveShare" });

const graph = ui.editor.graph;
const selectionModel = graph.getSelectionModel();

selectionModel.addListener(mxEvent.CHANGE, () => {
const cells = selectionModel.cells;
sendEvent({
event: "selectionChanged",
selectedCellIds: cells.map((c) => c.id),
});
});

const cursors = new Set<Cursor>();
const hightlights = new Highlights(graph);

window.addEventListener("message", (evt) => {
if (evt.source !== window.opener) {
return;
}
const data = JSON.parse(evt.data) as CustomDrawioAction;

switch (data.action) {
case "updateGhostCursors": {
for (const c of cursors) {
if (!data.cursors.some((c) => c.id === c.id)) {
cursors.delete(c);
c.dispose();
}
}
for (const c of data.cursors) {
const existing =
[...cursors].find(
(existingCursor) => existingCursor.id === c.id
) || new Cursor(graph.view.canvas, c.id);
cursors.add(existing);
existing.setPosition(transform(c.position));
}
break;
}
case "updateGhostSelections": {
const highlightInfos = new Array<HighlightInfo>();
for (const s of data.selections) {
for (const selectedCellId of s.selectedCellIds) {
const cell = graph.model.cells[selectedCellId];
highlightInfos.push({ cell, color: "#00ff00" });
}
}
hightlights.updateHighlights(highlightInfos);
}
}
});

function transform({ x, y }: { x: number; y: number }) {
const { scale, translate } = graph.view as any;
return { x: (x + translate.x) * scale, y: (y + translate.y) * scale };
}

function transformBack({ x, y }: { x: number; y: number }) {
const { scale, translate } = graph.view as any;
return { x: x / scale - translate.x, y: y / scale - translate.y };
}

graph.addMouseListener({
mouseMove: (graph: DrawioGraph, event: mxMouseEvent) => {
const pos = { x: event.graphX, y: event.graphY };
const graphPos = transformBack(pos);
sendEvent({ event: "cursorChanged", position: graphPos });
},
mouseDown: () => {},
mouseUp: () => {},
});
});

const svgns = "http://www.w3.org/2000/svg";

class Cursor {
private readonly g = document.createElementNS(svgns, "g");

constructor(canvas: SVGElement, public readonly id: string) {
canvas.appendChild(this.g);
this.g.setAttribute("pointer-events", "none");
this.g.innerHTML = `
<g transform="scale(0.06,0.06)">
<path fill="green" d="M302.189 329.126H196.105l55.831 135.993c3.889 9.428-.555 19.999-9.444 23.999l-49.165 21.427c-9.165 4-19.443-.571-23.332-9.714l-53.053-129.136-86.664 89.138C18.729 472.71 0 463.554 0 447.977V18.299C0 1.899 19.921-6.096 30.277 5.443l284.412 292.542c11.472 11.179 3.007 31.141-12.5 31.141z"/>
</g>
`;
}

public setPosition(pos: { x: number; y: number }) {
this.g.setAttribute("transform", `translate(${pos.x}, ${pos.y})`);
}

public dispose(): void {
this.g.remove();
}
}

interface HighlightInfo {
color: string;
cell: DrawioCell;
}

class Highlights {
private readonly highlights = new Map<
string,
{ info: HighlightInfo; instance: mxCellHighlight }
>();

constructor(private readonly graph: DrawioGraph) {}

private highlightInfoToStr(info: HighlightInfo): string {
return JSON.stringify({ color: info.color, cell: info.cell.id });
}

public updateHighlights(highlights: HighlightInfo[]): void {
const set = new Set(highlights.map((h) => this.highlightInfoToStr(h)));

for (const [key, h] of this.highlights) {
if (!set.has(key)) {
h.instance.destroy();
this.highlights.delete(key);
}
}

for (const h of highlights) {
const key = this.highlightInfoToStr(h);
if (!this.highlights.has(key)) {
const obj = {
info: h,
instance: new mxCellHighlight(this.graph, h.color, 8),
};
this.highlights.set(key, obj);
obj.instance.highlight(this.graph.view.getState(h.cell));
}
}
}
}
38 changes: 36 additions & 2 deletions drawio-custom-plugins/src/types.d.ts
@@ -1,6 +1,6 @@

declare type CustomDrawioAction = UpdateVerticesAction | AddVerticesAction | GetVerticesAction | LinkSelectedNodeWithDataAction | NodeSelectionEnabledAction;
declare type CustomDrawioEvent = NodeSelectedEvent | GetVerticesResultEvent | UpdateLocalStorage | PluginLoaded;
declare type CustomDrawioAction = UpdateVerticesAction | AddVerticesAction | GetVerticesAction | LinkSelectedNodeWithDataAction | NodeSelectionEnabledAction | UpdateGhostCursors | UpdateGhostSelections;
declare type CustomDrawioEvent = NodeSelectedEvent | GetVerticesResultEvent | UpdateLocalStorage | PluginLoaded | CursorChangedEvent | SelectionChangedEvent;

declare interface NodeSelectionEnabledAction {
action: "setNodeSelectionEnabled";
Expand Down Expand Up @@ -45,4 +45,38 @@ declare interface UpdateLocalStorage {

declare interface PluginLoaded {
event: "pluginLoaded";
pluginId: string;
}

// Liveshare

declare interface CursorChangedEvent {
event: "cursorChanged";
position: { x: number, y: number } | undefined;
}

declare interface UpdateGhostCursors {
action: "updateGhostCursors";
cursors: CursorUpdateInfo[];
}

declare interface SelectionChangedEvent {
event: "selectionChanged";
selectedCellIds: string[];
}

declare interface UpdateGhostSelections {
action: "updateGhostSelections";
selections: SelectionsUpdateInfo[]
}

declare interface CursorUpdateInfo {
name: string;
id: string;
position: { x: number, y: number };
}

declare interface SelectionsUpdateInfo {
id: string;
selectedCellIds: string[];
}
7 changes: 7 additions & 0 deletions drawio-custom-plugins/src/vscode.ts
@@ -0,0 +1,7 @@
export function sendEvent(data: CustomDrawioEvent) {
if (window.opener) {
window.opener.postMessage(JSON.stringify(data), "*");
} else {
console.log("sending >>>", data);
}
}
2 changes: 1 addition & 1 deletion drawio-custom-plugins/webpack.config.ts
Expand Up @@ -5,7 +5,7 @@ import { CleanWebpackPlugin } from "clean-webpack-plugin";
const r = (file: string) => path.resolve(__dirname, file);

module.exports = {
target: "node",
target: "web",
entry: r("./src/index"),
output: {
path: r("../dist/custom-drawio-plugins"),
Expand Down

0 comments on commit 6376917

Please sign in to comment.