[turbolizer] Turboshaft custom blocks/nodes data
Bug: v8:7327 Change-Id: I41faceac568a87cec4ae47ce2e4fc2c03822ddca Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3794649 Commit-Queue: Danylo Boiko <danielboyko02@gmail.com> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Cr-Commit-Position: refs/heads/main@{#82421}
This commit is contained in:
parent
573084572a
commit
e6804d0181
@ -165,6 +165,7 @@ body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
p {
|
||||
@ -357,8 +358,11 @@ input:hover,
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.viewpane {
|
||||
#multiview {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.viewpane {
|
||||
background-color: #FFFFFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -792,3 +796,7 @@ svg.history-svg-container .history-item-record tspan:hover {
|
||||
fill: #606060;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg.history-svg-container .current-history-item tspan {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -73,8 +73,8 @@ g.loop rect {
|
||||
fill: var(--select);
|
||||
}
|
||||
|
||||
.inline-node-properties tspan, .block-collapsed-label tspan {
|
||||
fill: #ca48f6;
|
||||
.inline-node-custom-data tspan, .block-collapsed-label tspan {
|
||||
fill: #c34ce8;
|
||||
}
|
||||
|
||||
g.turboshaft-node.input .inline-node-label tspan {
|
||||
@ -85,8 +85,8 @@ g.turboshaft-node.output .inline-node-label tspan {
|
||||
fill: var(--output);
|
||||
}
|
||||
|
||||
#layout-type-select {
|
||||
box-sizing: border-box;
|
||||
#custom-data-select {
|
||||
height: 1.5em;
|
||||
margin-left: 5px;
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
|
BIN
tools/turbolizer/img/toolbox/toggle-custom-data-icon.png
Normal file
BIN
tools/turbolizer/img/toolbox/toggle-custom-data-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 993 B |
Binary file not shown.
Before Width: | Height: | Size: 499 B |
@ -20,6 +20,7 @@ export abstract class Phase {
|
||||
export enum PhaseType {
|
||||
Graph = "graph",
|
||||
TurboshaftGraph = "turboshaft_graph",
|
||||
TurboshaftCustomData = "turboshaft_custom_data",
|
||||
Disassembly = "disassembly",
|
||||
Instructions = "instructions",
|
||||
Sequence = "sequence",
|
||||
|
29
tools/turbolizer/src/phases/turboshaft-custom-data-phase.ts
Normal file
29
tools/turbolizer/src/phases/turboshaft-custom-data-phase.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import { Phase, PhaseType } from "./phase";
|
||||
|
||||
export class TurboshaftCustomDataPhase extends Phase {
|
||||
dataTarget: DataTarget;
|
||||
data: Array<string>;
|
||||
|
||||
constructor(name: string, dataTarget: DataTarget, dataJSON) {
|
||||
super(name, PhaseType.TurboshaftCustomData);
|
||||
this.dataTarget = dataTarget;
|
||||
this.data = new Array<string>();
|
||||
this.parseDataFromJSON(dataJSON);
|
||||
}
|
||||
|
||||
private parseDataFromJSON(dataJSON): void {
|
||||
if (!dataJSON) return;
|
||||
for (const item of dataJSON) {
|
||||
this.data[item.key] = item.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum DataTarget {
|
||||
Nodes = "operations",
|
||||
Blocks = "blocks"
|
||||
}
|
@ -13,7 +13,7 @@ export class TurboshaftGraphBlock extends Node<TurboshaftGraphEdge<TurboshaftGra
|
||||
deferred: boolean;
|
||||
predecessors: Array<string>;
|
||||
nodes: Array<TurboshaftGraphNode>;
|
||||
showProperties: boolean;
|
||||
showCustomData: boolean;
|
||||
collapsed: boolean;
|
||||
collapsedLabel: string;
|
||||
collapsedLabelBox: { width: number, height: number };
|
||||
@ -30,14 +30,14 @@ export class TurboshaftGraphBlock extends Node<TurboshaftGraphEdge<TurboshaftGra
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
public getHeight(showProperties: boolean): number {
|
||||
public getHeight(showCustomData: boolean): number {
|
||||
if (this.collapsed) return this.labelBox.height + this.collapsedLabelBox.height;
|
||||
|
||||
if (this.showProperties != showProperties) {
|
||||
if (this.showCustomData != showCustomData) {
|
||||
this.height = this.nodes.reduce<number>((accumulator: number, node: TurboshaftGraphNode) => {
|
||||
return accumulator + node.getHeight(showProperties);
|
||||
return accumulator + node.getHeight(showCustomData);
|
||||
}, this.labelBox.height);
|
||||
this.showProperties = showProperties;
|
||||
this.showCustomData = showCustomData;
|
||||
}
|
||||
|
||||
return this.height;
|
||||
@ -58,7 +58,7 @@ export class TurboshaftGraphBlock extends Node<TurboshaftGraphEdge<TurboshaftGra
|
||||
public compressHeight(): void {
|
||||
if (this.collapsed) {
|
||||
this.height = this.getHeight(null);
|
||||
this.showProperties = null;
|
||||
this.showCustomData = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,32 +12,25 @@ export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGrap
|
||||
title: string;
|
||||
block: TurboshaftGraphBlock;
|
||||
opPropertiesType: OpPropertiesType;
|
||||
properties: string;
|
||||
propertiesBox: { width: number, height: number };
|
||||
|
||||
constructor(id: number, title: string, block: TurboshaftGraphBlock,
|
||||
opPropertiesType: OpPropertiesType, properties: string) {
|
||||
opPropertiesType: OpPropertiesType) {
|
||||
super(id);
|
||||
this.title = title;
|
||||
this.block = block;
|
||||
this.opPropertiesType = opPropertiesType;
|
||||
this.properties = properties;
|
||||
this.propertiesBox = measureText(this.properties);
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
public getHeight(showProperties: boolean): number {
|
||||
if (this.properties && showProperties) {
|
||||
return this.labelBox.height + this.propertiesBox.height;
|
||||
}
|
||||
return this.labelBox.height;
|
||||
public getHeight(showCustomData: boolean): number {
|
||||
return showCustomData ? this.labelBox.height * 2 : this.labelBox.height;
|
||||
}
|
||||
|
||||
public getWidth(): number {
|
||||
return Math.max(this.inputs.length * C.NODE_INPUT_WIDTH, this.labelBox.width);
|
||||
}
|
||||
|
||||
public initDisplayLabel() {
|
||||
public initDisplayLabel(): void {
|
||||
this.displayLabel = this.getInlineLabel();
|
||||
this.labelBox = measureText(this.displayLabel);
|
||||
}
|
||||
@ -50,21 +43,13 @@ export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGrap
|
||||
if (this.outputs.length > 0) {
|
||||
title += `\nOutputs: ${this.outputs.map(i => i.target.id).join(", ")}`;
|
||||
}
|
||||
const opPropertiesStr = this.properties.length > 0 ? this.properties : "No op properties";
|
||||
return `${title}\n${opPropertiesStr}`;
|
||||
return title;
|
||||
}
|
||||
|
||||
public getInlineLabel(): string {
|
||||
if (this.inputs.length == 0) return `${this.id} ${this.title}`;
|
||||
return `${this.id} ${this.title}(${this.inputs.map(i => i.source.id).join(",")})`;
|
||||
}
|
||||
|
||||
public getReadableProperties(blockWidth: number): string {
|
||||
if (blockWidth > this.propertiesBox.width) return this.properties;
|
||||
const widthOfOneSymbol = Math.floor(this.propertiesBox.width / this.properties.length);
|
||||
const lengthOfReadableProperties = Math.floor(blockWidth / widthOfOneSymbol);
|
||||
return `${this.properties.slice(0, lengthOfReadableProperties - 3)}..`;
|
||||
}
|
||||
}
|
||||
|
||||
export enum OpPropertiesType {
|
||||
|
@ -6,19 +6,22 @@ import { GraphStateType, Phase, PhaseType } from "../phase";
|
||||
import { TurboshaftGraphNode } from "./turboshaft-graph-node";
|
||||
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
|
||||
import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
|
||||
import { DataTarget, TurboshaftCustomDataPhase } from "../turboshaft-custom-data-phase";
|
||||
|
||||
export class TurboshaftGraphPhase extends Phase {
|
||||
data: TurboshaftGraphData;
|
||||
customData: TurboshaftCustomData;
|
||||
stateType: GraphStateType;
|
||||
nodeIdToNodeMap: Array<TurboshaftGraphNode>;
|
||||
blockIdToBlockMap: Array<TurboshaftGraphBlock>;
|
||||
rendered: boolean;
|
||||
propertiesShowed: boolean;
|
||||
customDataShowed: boolean;
|
||||
transform: { x: number, y: number, scale: number };
|
||||
|
||||
constructor(name: string, dataJson) {
|
||||
super(name, PhaseType.TurboshaftGraph);
|
||||
this.stateType = GraphStateType.NeedToFullRebuild;
|
||||
this.customData = new TurboshaftCustomData();
|
||||
this.nodeIdToNodeMap = new Array<TurboshaftGraphNode>();
|
||||
this.blockIdToBlockMap = new Array<TurboshaftGraphBlock>();
|
||||
this.rendered = false;
|
||||
@ -53,7 +56,7 @@ export class TurboshaftGraphPhase extends Phase {
|
||||
for (const nodeJson of nodesJson) {
|
||||
const block = this.blockIdToBlockMap[nodeJson.block_id];
|
||||
const node = new TurboshaftGraphNode(nodeJson.id, nodeJson.title,
|
||||
block, nodeJson.op_properties_type, nodeJson.properties);
|
||||
block, nodeJson.op_properties_type);
|
||||
block.nodes.push(node);
|
||||
this.data.nodes.push(node);
|
||||
this.nodeIdToNodeMap[node.identifier()] = node;
|
||||
@ -89,3 +92,43 @@ export class TurboshaftGraphData {
|
||||
this.blocks = new Array<TurboshaftGraphBlock>();
|
||||
}
|
||||
}
|
||||
|
||||
export class TurboshaftCustomData {
|
||||
nodes: Map<string, TurboshaftCustomDataPhase>;
|
||||
blocks: Map<string, TurboshaftCustomDataPhase>;
|
||||
|
||||
constructor() {
|
||||
this.nodes = new Map<string, TurboshaftCustomDataPhase>();
|
||||
this.blocks = new Map<string, TurboshaftCustomDataPhase>();
|
||||
}
|
||||
|
||||
public addCustomData(customDataPhase: TurboshaftCustomDataPhase): void {
|
||||
switch (customDataPhase.dataTarget) {
|
||||
case DataTarget.Nodes:
|
||||
this.nodes.set(customDataPhase.name, customDataPhase);
|
||||
break;
|
||||
case DataTarget.Blocks:
|
||||
this.blocks.set(customDataPhase.name, customDataPhase);
|
||||
break;
|
||||
default:
|
||||
throw "Unsupported turboshaft custom data target type";
|
||||
}
|
||||
}
|
||||
|
||||
public getTitle(key: number, dataTarget: DataTarget): string {
|
||||
switch (dataTarget) {
|
||||
case DataTarget.Nodes:
|
||||
return this.concatCustomData(key, this.nodes);
|
||||
case DataTarget.Blocks:
|
||||
return this.concatCustomData(key, this.blocks);
|
||||
}
|
||||
}
|
||||
|
||||
private concatCustomData(key: number, items: Map<string, TurboshaftCustomDataPhase>): string {
|
||||
let customData = "";
|
||||
for (const [name, dataPhase] of items.entries()) {
|
||||
customData += `\n${name}: ${dataPhase.data[key] ?? ""}`;
|
||||
}
|
||||
return customData;
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,12 @@ import { SequencePhase } from "./phases/sequence-phase";
|
||||
import { BytecodeOrigin } from "./origin";
|
||||
import { Source } from "./source";
|
||||
import { NodeLabel } from "./node-label";
|
||||
import { TurboshaftCustomDataPhase } from "./phases/turboshaft-custom-data-phase";
|
||||
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
||||
|
||||
export type GenericPosition = SourcePosition | BytecodePosition;
|
||||
export type GenericPhase = GraphPhase | TurboshaftGraphPhase | DisassemblyPhase
|
||||
| InstructionsPhase | SchedulePhase | SequencePhase;
|
||||
export type GenericPhase = GraphPhase | TurboshaftGraphPhase | TurboshaftCustomDataPhase
|
||||
| DisassemblyPhase | InstructionsPhase | SchedulePhase | SequencePhase;
|
||||
|
||||
export class SourceResolver {
|
||||
nodePositionMap: Array<GenericPosition>;
|
||||
@ -129,6 +130,7 @@ export class SourceResolver {
|
||||
|
||||
public parsePhases(phasesJson): void {
|
||||
const nodeLabelMap = new Array<NodeLabel>();
|
||||
let lastTurboshaftGraphPhase: TurboshaftGraphPhase = null;
|
||||
for (const [, genericPhase] of Object.entries<GenericPhase>(phasesJson)) {
|
||||
switch (genericPhase.type) {
|
||||
case PhaseType.Disassembly:
|
||||
@ -179,6 +181,13 @@ export class SourceResolver {
|
||||
castedTurboshaftGraph.data);
|
||||
this.phaseNames.set(turboshaftGraphPhase.name, this.phases.length);
|
||||
this.phases.push(turboshaftGraphPhase);
|
||||
lastTurboshaftGraphPhase = turboshaftGraphPhase;
|
||||
break;
|
||||
case PhaseType.TurboshaftCustomData:
|
||||
const castedCustomData = camelize(genericPhase) as TurboshaftCustomDataPhase;
|
||||
const customDataPhase = new TurboshaftCustomDataPhase(castedCustomData.name,
|
||||
castedCustomData.dataTarget, castedCustomData.data);
|
||||
lastTurboshaftGraphPhase?.customData?.addCustomData(customDataPhase);
|
||||
break;
|
||||
default:
|
||||
throw "Unsupported phase type";
|
||||
|
@ -102,7 +102,7 @@ window.onload = function () {
|
||||
historyView.show();
|
||||
} catch (err) {
|
||||
if (window.confirm("Error: Exception during load of TurboFan JSON file:\n" +
|
||||
`error: ${err.message} \nDo you want to clear session storage?`)) {
|
||||
`error: ${err} \nDo you want to clear session storage?`)) {
|
||||
window.sessionStorage.clear();
|
||||
}
|
||||
return;
|
||||
|
@ -21,10 +21,10 @@ export class TurboshaftGraphLayout {
|
||||
this.maxRank = 0;
|
||||
}
|
||||
|
||||
public rebuild(showProperties: boolean): void {
|
||||
public rebuild(showCustomData: boolean): void {
|
||||
switch (this.graph.graphPhase.stateType) {
|
||||
case GraphStateType.NeedToFullRebuild:
|
||||
this.fullRebuild(showProperties);
|
||||
this.fullRebuild(showCustomData);
|
||||
break;
|
||||
case GraphStateType.Cached:
|
||||
this.cachedRebuild();
|
||||
@ -35,7 +35,7 @@ export class TurboshaftGraphLayout {
|
||||
this.graph.graphPhase.rendered = true;
|
||||
}
|
||||
|
||||
private fullRebuild(showProperties: boolean): void {
|
||||
private fullRebuild(showCustomData: boolean): void {
|
||||
this.startTime = performance.now();
|
||||
this.maxRank = 0;
|
||||
this.visitOrderWithinRank = 0;
|
||||
@ -46,8 +46,8 @@ export class TurboshaftGraphLayout {
|
||||
const visited = new Array<boolean>();
|
||||
blocks.forEach((block: TurboshaftGraphBlock) => this.dfsRankOrder(visited, block));
|
||||
|
||||
const rankSets = this.getRankSets(showProperties);
|
||||
this.placeBlocks(rankSets, showProperties);
|
||||
const rankSets = this.getRankSets(showCustomData);
|
||||
this.placeBlocks(rankSets, showCustomData);
|
||||
this.calculateBackEdgeNumbers();
|
||||
this.graph.graphPhase.stateType = GraphStateType.Cached;
|
||||
}
|
||||
@ -133,8 +133,8 @@ export class TurboshaftGraphLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private getRankSets(showProperties: boolean): Array<Array<TurboshaftGraphBlock>> {
|
||||
const ranksMaxBlockHeight = this.graph.getRanksMaxBlockHeight(showProperties);
|
||||
private getRankSets(showCustomData: boolean): Array<Array<TurboshaftGraphBlock>> {
|
||||
const ranksMaxBlockHeight = this.graph.getRanksMaxBlockHeight(showCustomData);
|
||||
const rankSets = new Array<Array<TurboshaftGraphBlock>>();
|
||||
for (const block of this.graph.blocks()) {
|
||||
block.y = ranksMaxBlockHeight.slice(1, block.rank).reduce<number>((accumulator, current) => {
|
||||
@ -149,13 +149,13 @@ export class TurboshaftGraphLayout {
|
||||
return rankSets;
|
||||
}
|
||||
|
||||
private placeBlocks(rankSets: Array<Array<TurboshaftGraphBlock>>, showProperties: boolean): void {
|
||||
private placeBlocks(rankSets: Array<Array<TurboshaftGraphBlock>>, showCustomData: boolean): void {
|
||||
// Iterate backwards from highest to lowest rank, placing blocks so that they
|
||||
// spread out from the "center" as much as possible while still being
|
||||
// compact and not overlapping live input lines.
|
||||
rankSets.reverse().forEach((rankSet: Array<TurboshaftGraphBlock>) => {
|
||||
for (const block of rankSet) {
|
||||
this.layoutOccupation.clearOutputs(block, showProperties);
|
||||
this.layoutOccupation.clearOutputs(block, showCustomData);
|
||||
}
|
||||
|
||||
this.traceOccupation("After clearing outputs");
|
||||
@ -178,7 +178,7 @@ export class TurboshaftGraphLayout {
|
||||
this.traceOccupation("After clearing blocks");
|
||||
|
||||
for (const block of rankSet) {
|
||||
this.layoutOccupation.occupyInputs(block, showProperties);
|
||||
this.layoutOccupation.occupyInputs(block, showCustomData);
|
||||
}
|
||||
|
||||
this.traceOccupation("After occupying inputs and determining bounding box");
|
||||
|
@ -4,19 +4,25 @@
|
||||
|
||||
import * as C from "./common/constants";
|
||||
import { MovableContainer } from "./movable-container";
|
||||
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
||||
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
|
||||
import { TurboshaftGraphBlock } from "./phases/turboshaft-graph-phase/turboshaft-graph-block";
|
||||
import { TurboshaftGraphEdge } from "./phases/turboshaft-graph-phase/turboshaft-graph-edge";
|
||||
import { DataTarget } from "./phases/turboshaft-custom-data-phase";
|
||||
import {
|
||||
TurboshaftCustomData,
|
||||
TurboshaftGraphPhase
|
||||
} from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
||||
|
||||
export class TurboshaftGraph extends MovableContainer<TurboshaftGraphPhase> {
|
||||
blockMap: Array<TurboshaftGraphBlock>;
|
||||
nodeMap: Array<TurboshaftGraphNode>;
|
||||
customData: TurboshaftCustomData;
|
||||
|
||||
constructor(graphPhase: TurboshaftGraphPhase) {
|
||||
super(graphPhase);
|
||||
this.blockMap = graphPhase.blockIdToBlockMap;
|
||||
this.nodeMap = graphPhase.nodeIdToNodeMap;
|
||||
this.customData = graphPhase.customData;
|
||||
}
|
||||
|
||||
public *blocks(func = (b: TurboshaftGraphBlock) => true) {
|
||||
@ -53,7 +59,7 @@ export class TurboshaftGraph extends MovableContainer<TurboshaftGraphPhase> {
|
||||
}
|
||||
}
|
||||
|
||||
public redetermineGraphBoundingBox(showProperties: boolean):
|
||||
public redetermineGraphBoundingBox(showCustomData: boolean):
|
||||
[[number, number], [number, number]] {
|
||||
this.minGraphX = 0;
|
||||
this.maxGraphNodeX = 1;
|
||||
@ -65,7 +71,7 @@ export class TurboshaftGraph extends MovableContainer<TurboshaftGraphPhase> {
|
||||
this.maxGraphNodeX = Math.max(this.maxGraphNodeX, block.x + block.getWidth());
|
||||
|
||||
this.minGraphY = Math.min(this.minGraphY, block.y - C.NODE_INPUT_WIDTH);
|
||||
this.maxGraphY = Math.max(this.maxGraphY, block.y + block.getHeight(showProperties)
|
||||
this.maxGraphY = Math.max(this.maxGraphY, block.y + block.getHeight(showCustomData)
|
||||
+ C.NODE_INPUT_WIDTH);
|
||||
}
|
||||
|
||||
@ -80,12 +86,30 @@ export class TurboshaftGraph extends MovableContainer<TurboshaftGraphPhase> {
|
||||
];
|
||||
}
|
||||
|
||||
public getRanksMaxBlockHeight(showProperties: boolean): Array<number> {
|
||||
public hasCustomData(customData: string, dataTarget: DataTarget): boolean {
|
||||
switch (dataTarget) {
|
||||
case DataTarget.Nodes:
|
||||
return this.customData.nodes.has(customData);
|
||||
case DataTarget.Blocks:
|
||||
return this.customData.blocks.has(customData);
|
||||
}
|
||||
}
|
||||
|
||||
public getCustomData(customData: string, key: number, dataTarget: DataTarget): string {
|
||||
switch (dataTarget) {
|
||||
case DataTarget.Nodes:
|
||||
return this.customData.nodes.get(customData).data[key];
|
||||
case DataTarget.Blocks:
|
||||
return this.customData.blocks.get(customData).data[key];
|
||||
}
|
||||
}
|
||||
|
||||
public getRanksMaxBlockHeight(showCustomData: boolean): Array<number> {
|
||||
const ranksMaxBlockHeight = new Array<number>();
|
||||
|
||||
for (const block of this.blocks()) {
|
||||
ranksMaxBlockHeight[block.rank] = Math.max(ranksMaxBlockHeight[block.rank] ?? 0,
|
||||
block.getHeight(showProperties));
|
||||
block.getHeight(showCustomData));
|
||||
}
|
||||
|
||||
return ranksMaxBlockHeight;
|
||||
|
@ -181,6 +181,7 @@ export class HistoryView extends View {
|
||||
this.historyList
|
||||
.append("text")
|
||||
.classed("history-item history-item-record", true)
|
||||
.classed("current-history-item", record.changes.has(HistoryChange.Current))
|
||||
.attr("dy", recordY)
|
||||
.attr("dx", this.labelBox.height * 0.75)
|
||||
.append("tspan")
|
||||
@ -389,7 +390,11 @@ export class HistoryView extends View {
|
||||
this.phaseIdToHistory.set(phaseId, new PhaseHistory(phaseId));
|
||||
}
|
||||
this.phaseIdToHistory.get(phaseId).addChange(node, change);
|
||||
this.maxNodeWidth = Math.max(this.maxNodeWidth, node.labelBox.width);
|
||||
if (change == HistoryChange.Current) {
|
||||
this.maxNodeWidth = Math.max(this.maxNodeWidth, node.labelBox.width * 1.07);
|
||||
} else {
|
||||
this.maxNodeWidth = Math.max(this.maxNodeWidth, node.labelBox.width);
|
||||
}
|
||||
}
|
||||
|
||||
private clear(): void {
|
||||
|
@ -252,6 +252,13 @@ export abstract class MovableView<GraphType extends Graph | TurboshaftGraph> ext
|
||||
})];
|
||||
}
|
||||
|
||||
protected createImgToggleInput(id: string, title: string, initState: boolean, onClick):
|
||||
HTMLElement {
|
||||
const input = this.createImgInput(id, title, onClick);
|
||||
input.classList.toggle("button-input-toggled", initState);
|
||||
return input;
|
||||
}
|
||||
|
||||
private deleteContent(): void {
|
||||
for (const item of this.toolbox.querySelectorAll(".graph-toolbox-item")) {
|
||||
item.parentElement.removeChild(item);
|
||||
@ -283,13 +290,6 @@ export abstract class MovableView<GraphType extends Graph | TurboshaftGraph> ext
|
||||
input.addEventListener("click", onClick);
|
||||
return input;
|
||||
}
|
||||
|
||||
private createImgToggleInput(id: string, title: string, initState: boolean, onClick):
|
||||
HTMLElement {
|
||||
const input = this.createImgInput(id, title, onClick);
|
||||
input.classList.toggle("button-input-toggled", initState);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
export class MovableViewState {
|
||||
@ -312,12 +312,12 @@ export class MovableViewState {
|
||||
storageSetItem("toggle-types", value);
|
||||
}
|
||||
|
||||
public get showProperties(): boolean {
|
||||
return storageGetItem("toggle-properties", false);
|
||||
public get showCustomData(): boolean {
|
||||
return storageGetItem("toggle-custom-data", false);
|
||||
}
|
||||
|
||||
public set showProperties(value: boolean) {
|
||||
storageSetItem("toggle-properties", value);
|
||||
public set showCustomData(value: boolean) {
|
||||
storageSetItem("toggle-custom-data", value);
|
||||
}
|
||||
|
||||
public get cacheLayout(): boolean {
|
||||
|
@ -4,7 +4,13 @@
|
||||
|
||||
import * as C from "../common/constants";
|
||||
import * as d3 from "d3";
|
||||
import { copyToClipboard, partial, storageSetItem } from "../common/util";
|
||||
import {
|
||||
copyToClipboard,
|
||||
measureText,
|
||||
partial,
|
||||
storageGetItem,
|
||||
storageSetItem
|
||||
} from "../common/util";
|
||||
import { MovableView } from "./movable-view";
|
||||
import { SelectionBroker } from "../selection/selection-broker";
|
||||
import { SelectionMap } from "../selection/selection-map";
|
||||
@ -14,7 +20,11 @@ import { TurboshaftGraph } from "../turboshaft-graph";
|
||||
import { TurboshaftGraphLayout } from "../turboshaft-graph-layout";
|
||||
import { GraphStateType } from "../phases/graph-phase/graph-phase";
|
||||
import { SelectionStorage } from "../selection/selection-storage";
|
||||
import { TurboshaftGraphPhase } from "../phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
||||
import { DataTarget } from "../phases/turboshaft-custom-data-phase";
|
||||
import {
|
||||
TurboshaftCustomData,
|
||||
TurboshaftGraphPhase
|
||||
} from "../phases/turboshaft-graph-phase/turboshaft-graph-phase";
|
||||
import {
|
||||
BlockSelectionHandler,
|
||||
ClearableHandler,
|
||||
@ -79,12 +89,12 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
partial(this.changeSelectedCollapsingAction, this, false));
|
||||
this.addImgInput("zoom-selection", "zoom selection",
|
||||
partial(this.zoomSelectionAction, this));
|
||||
this.addToggleImgInput("toggle-properties", "toggle properties",
|
||||
this.state.showProperties, partial(this.togglePropertiesAction, this));
|
||||
this.addToggleImgInput("toggle-cache-layout", "toggle saving graph layout",
|
||||
this.state.cacheLayout, partial(this.toggleLayoutCachingAction, this));
|
||||
|
||||
this.phaseName = data.name;
|
||||
this.addCustomDataSelect(data.customData);
|
||||
|
||||
const adaptedSelection = this.createGraph(data, rememberedSelection);
|
||||
this.broker.addNodeHandler(this.nodeSelectionHandler);
|
||||
this.broker.addBlockHandler(this.blockSelectionHandler);
|
||||
@ -100,7 +110,8 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.graphLayout.graph.graphPhase.propertiesShowed != this.state.showProperties) {
|
||||
const customDataShowed = this.graph.graphPhase.customDataShowed;
|
||||
if (customDataShowed != null && customDataShowed != this.nodesCustomDataShowed()) {
|
||||
this.compressLayoutAction(this);
|
||||
}
|
||||
}
|
||||
@ -181,9 +192,9 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
const reg = new RegExp(query);
|
||||
const filterFunction = (node: TurboshaftGraphNode) => {
|
||||
if (!onlyVisible) node.block.collapsed = false;
|
||||
return (!onlyVisible || !node.block.collapsed) && (reg.exec(node.displayLabel) !== null ||
|
||||
(this.state.showProperties && reg.exec(node.properties)) ||
|
||||
reg.exec(node.getTitle()));
|
||||
const customDataTitle = this.graph.customData.getTitle(node.id, DataTarget.Nodes);
|
||||
return (!onlyVisible || !node.block.collapsed) &&
|
||||
reg.exec(`${node.getTitle()}${customDataTitle}`);
|
||||
};
|
||||
|
||||
const selection = this.searchNodes(filterFunction, e, onlyVisible);
|
||||
@ -198,7 +209,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this.graphLayout.graph.graphPhase.propertiesShowed = this.state.showProperties;
|
||||
this.graph.graphPhase.customDataShowed = this.nodesCustomDataShowed();
|
||||
this.broker.deleteBlockHandler(this.blockSelectionHandler);
|
||||
super.hide();
|
||||
}
|
||||
@ -317,13 +328,51 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
: "Layout turboshaft graph";
|
||||
|
||||
console.time(layoutMessage);
|
||||
this.graphLayout.rebuild(this.state.showProperties);
|
||||
const extent = this.graph.redetermineGraphBoundingBox(this.state.showProperties);
|
||||
this.graphLayout.rebuild(this.nodesCustomDataShowed());
|
||||
const extent = this.graph.redetermineGraphBoundingBox(this.nodesCustomDataShowed());
|
||||
this.panZoom.translateExtent(extent);
|
||||
this.minScale();
|
||||
console.timeEnd(layoutMessage);
|
||||
}
|
||||
|
||||
private addCustomDataSelect(customData: TurboshaftCustomData): void {
|
||||
const keys = Array.from(customData.nodes.keys());
|
||||
if (keys.length == 0) return;
|
||||
|
||||
const select = document.createElement("select") as HTMLSelectElement;
|
||||
select.setAttribute("id", "custom-data-select");
|
||||
select.setAttribute("class", "graph-toolbox-item");
|
||||
select.setAttribute("title", "custom data");
|
||||
|
||||
const checkBox = this.createImgToggleInput("toggle-custom-data",
|
||||
"toggle custom data visibility", this.state.showCustomData,
|
||||
partial(this.toggleCustomDataAction, this));
|
||||
|
||||
for (const key of keys) {
|
||||
const option = document.createElement("option");
|
||||
option.text = key;
|
||||
select.add(option);
|
||||
}
|
||||
|
||||
const storageKey = this.customDataStorageKey();
|
||||
const indexOfSelected = keys.indexOf(storageGetItem(storageKey, null, false));
|
||||
if (indexOfSelected != -1) {
|
||||
select.selectedIndex = indexOfSelected;
|
||||
} else {
|
||||
storageSetItem(storageKey, keys[0]);
|
||||
}
|
||||
|
||||
const view = this;
|
||||
select.onchange = function (this: HTMLSelectElement) {
|
||||
const selectedCustomData = select.options[this.selectedIndex].text;
|
||||
storageSetItem(storageKey, selectedCustomData);
|
||||
view.updateGraphVisibility();
|
||||
};
|
||||
|
||||
this.toolbox.appendChild(select);
|
||||
this.toolbox.appendChild(checkBox);
|
||||
}
|
||||
|
||||
private updateBlockLocation(block: TurboshaftGraphBlock): void {
|
||||
this.visibleBlocks
|
||||
.selectAll<SVGGElement, TurboshaftGraphBlock>(".turboshaft-block")
|
||||
@ -333,7 +382,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
this.visibleEdges
|
||||
.selectAll<SVGPathElement, TurboshaftGraphEdge<TurboshaftGraphBlock>>("path")
|
||||
.filter(edge => edge.target === block || edge.source === block)
|
||||
.attr("d", edge => edge.generatePath(this.graph, this.state.showProperties));
|
||||
.attr("d", edge => edge.generatePath(this.graph, this.nodesCustomDataShowed()));
|
||||
}
|
||||
|
||||
private updateVisibleBlocksAndEdges(): void {
|
||||
@ -341,9 +390,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
const iconsPath = "img/turboshaft/";
|
||||
|
||||
// select existing edges
|
||||
const filteredEdges = [
|
||||
...this.graph.blocksEdges(_ => this.graph.isRendered())
|
||||
];
|
||||
const filteredEdges = [...view.graph.blocksEdges(_ => view.graph.isRendered())];
|
||||
|
||||
const selEdges = view.visibleEdges
|
||||
.selectAll<SVGPathElement, TurboshaftGraphEdge<TurboshaftGraphBlock>>("path")
|
||||
@ -372,9 +419,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
newAndOldEdges.classed("hidden", edge => !edge.isVisible());
|
||||
|
||||
// select existing blocks
|
||||
const filteredBlocks = [
|
||||
...this.graph.blocks(_ => this.graph.isRendered())
|
||||
];
|
||||
const filteredBlocks = [...view.graph.blocks(_ => view.graph.isRendered())];
|
||||
const allBlocks = view.visibleBlocks
|
||||
.selectAll<SVGGElement, TurboshaftGraphBlock>(".turboshaft-block");
|
||||
const selBlocks = allBlocks.data(filteredBlocks, block => block.toString());
|
||||
@ -419,7 +464,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
.attr("rx", C.TURBOSHAFT_BLOCK_BORDER_RADIUS)
|
||||
.attr("ry", C.TURBOSHAFT_BLOCK_BORDER_RADIUS)
|
||||
.attr("width", block => block.getWidth())
|
||||
.attr("height", block => block.getHeight(view.state.showProperties));
|
||||
.attr("height", block => block.getHeight(view.nodesCustomDataShowed()));
|
||||
|
||||
newBlocks.each(function (block: TurboshaftGraphBlock) {
|
||||
const svg = d3.select<SVGGElement, TurboshaftGraphBlock>(this);
|
||||
@ -429,7 +474,9 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("x", block.getWidth() / 2)
|
||||
.append("tspan")
|
||||
.text(block.displayLabel);
|
||||
.text(block.displayLabel)
|
||||
.append("title")
|
||||
.text(view.graph.customData.getTitle(block.id, DataTarget.Blocks));
|
||||
|
||||
svg
|
||||
.append("text")
|
||||
@ -462,7 +509,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
.classed("selected", block => view.state.blocksSelection.isSelected(block))
|
||||
.attr("transform", block => `translate(${block.x},${block.y})`)
|
||||
.select("rect")
|
||||
.attr("height", block => block.getHeight(view.state.showProperties));
|
||||
.attr("height", block => block.getHeight(view.nodesCustomDataShowed()));
|
||||
|
||||
newAndOldBlocks.select("image")
|
||||
.attr("xlink:href", block => `${iconsPath}collapse_${block.collapsed ? "down" : "up"}.svg`);
|
||||
@ -470,7 +517,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
newAndOldBlocks.select(".block-collapsed-label")
|
||||
.attr("visibility", block => block.collapsed ? "visible" : "hidden");
|
||||
|
||||
newAndOldEdges.attr("d", edge => edge.generatePath(this.graph, view.state.showProperties));
|
||||
newAndOldEdges.attr("d", edge => edge.generatePath(view.graph, view.nodesCustomDataShowed()));
|
||||
}
|
||||
|
||||
private appendInlineNodes(svg: d3.Selection<SVGGElement, TurboshaftGraphBlock, any, any>,
|
||||
@ -493,6 +540,9 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
let nodeY = block.labelBox.height;
|
||||
const blockWidth = block.getWidth();
|
||||
const view = this;
|
||||
const customData = this.graph.customData;
|
||||
const storageKey = this.customDataStorageKey();
|
||||
const selectedCustomData = storageGetItem(storageKey, null, false);
|
||||
newNodes.each(function (node: TurboshaftGraphNode) {
|
||||
const nodeSvg = d3.select(this);
|
||||
nodeSvg
|
||||
@ -504,7 +554,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
.append("tspan")
|
||||
.text(node.displayLabel)
|
||||
.append("title")
|
||||
.text(node.getTitle());
|
||||
.text(`${node.getTitle()}${customData.getTitle(node.id, DataTarget.Nodes)}`);
|
||||
|
||||
nodeSvg
|
||||
.on("mouseenter", (node: TurboshaftGraphNode) => {
|
||||
@ -531,17 +581,18 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
d3.event.stopPropagation();
|
||||
});
|
||||
nodeY += node.labelBox.height;
|
||||
if (node.properties) {
|
||||
if (view.graph.customData.nodes.size > 0) {
|
||||
const customData = view.graph.getCustomData(selectedCustomData, node.id, DataTarget.Nodes);
|
||||
nodeSvg
|
||||
.append("text")
|
||||
.attr("dx", C.TURBOSHAFT_NODE_X_INDENT)
|
||||
.classed("inline-node-properties", true)
|
||||
.classed("inline-node-custom-data", true)
|
||||
.attr("dy", nodeY)
|
||||
.append("tspan")
|
||||
.text(node.getReadableProperties(blockWidth))
|
||||
.text(view.getReadableString(customData, blockWidth))
|
||||
.append("title")
|
||||
.text(node.properties);
|
||||
nodeY += node.propertiesBox.height;
|
||||
.text(customData);
|
||||
nodeY += node.labelBox.height;
|
||||
}
|
||||
});
|
||||
|
||||
@ -550,19 +601,21 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
}
|
||||
|
||||
private updateInlineNodes(): void {
|
||||
const view = this;
|
||||
const state = this.state;
|
||||
const storageKey = this.customDataStorageKey();
|
||||
const selectedCustomData = storageGetItem(storageKey, null, false);
|
||||
let totalHeight = 0;
|
||||
let blockId = 0;
|
||||
this.visibleNodes.each(function (node: TurboshaftGraphNode) {
|
||||
view.visibleNodes.each(function (node: TurboshaftGraphNode) {
|
||||
const nodeSvg = d3.select(this);
|
||||
const showCustomData = view.nodesCustomDataShowed();
|
||||
if (blockId != node.block.id) {
|
||||
blockId = node.block.id;
|
||||
totalHeight = 0;
|
||||
}
|
||||
totalHeight += node.getHeight(state.showProperties);
|
||||
const nodeSvg = d3.select(this);
|
||||
const nodeY = state.showProperties && node.properties
|
||||
? totalHeight - node.labelBox.height
|
||||
: totalHeight;
|
||||
totalHeight += node.getHeight(showCustomData);
|
||||
const nodeY = showCustomData ? totalHeight - node.labelBox.height : totalHeight;
|
||||
|
||||
nodeSvg
|
||||
.select(".inline-node-label")
|
||||
@ -570,9 +623,18 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
.attr("dy", nodeY)
|
||||
.attr("visibility", !node.block.collapsed ? "visible" : "hidden");
|
||||
|
||||
nodeSvg
|
||||
.select(".inline-node-properties")
|
||||
.attr("visibility", !node.block.collapsed && state.showProperties ? "visible" : "hidden");
|
||||
const svgNodeCustomData = nodeSvg
|
||||
.select(".inline-node-custom-data")
|
||||
.attr("visibility", !node.block.collapsed && showCustomData ? "visible" : "hidden");
|
||||
|
||||
if (!node.block.collapsed && showCustomData) {
|
||||
const customData = view.graph.getCustomData(selectedCustomData, node.id, DataTarget.Nodes);
|
||||
svgNodeCustomData
|
||||
.select("tspan")
|
||||
.text(view.getReadableString(customData, node.block.width))
|
||||
.append("title")
|
||||
.text(customData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -590,7 +652,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
}
|
||||
if (block.outputs.length > 0) {
|
||||
const x = block.getOutputX();
|
||||
const y = block.getHeight(this.state.showProperties) + C.DEFAULT_NODE_BUBBLE_RADIUS;
|
||||
const y = block.getHeight(this.nodesCustomDataShowed()) + C.DEFAULT_NODE_BUBBLE_RADIUS;
|
||||
svg.append("circle")
|
||||
.classed("filledBubbleStyle", true)
|
||||
.attr("id", `ob,${block.id}`)
|
||||
@ -601,12 +663,12 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
|
||||
private updateInputAndOutputBubbles(): void {
|
||||
const view = this;
|
||||
this.visibleBubbles.each(function () {
|
||||
view.visibleBubbles.each(function () {
|
||||
const components = this.id.split(",");
|
||||
if (components[0] === "ob") {
|
||||
const from = view.graph.blockMap[components[1]];
|
||||
const x = from.getOutputX();
|
||||
const y = from.getHeight(view.state.showProperties) + C.DEFAULT_NODE_BUBBLE_RADIUS;
|
||||
const y = from.getHeight(view.nodesCustomDataShowed()) + C.DEFAULT_NODE_BUBBLE_RADIUS;
|
||||
this.setAttribute("transform", `translate(${x},${y})`);
|
||||
}
|
||||
});
|
||||
@ -628,8 +690,8 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
maxX = maxX ? Math.max(maxX, block.x + block.getWidth()) : block.x + block.getWidth();
|
||||
minY = minY ? Math.min(minY, block.y) : block.y;
|
||||
maxY = maxY
|
||||
? Math.max(maxY, block.y + block.getHeight(this.state.showProperties))
|
||||
: block.y + block.getHeight(this.state.showProperties);
|
||||
? Math.max(maxY, block.y + block.getHeight(this.nodesCustomDataShowed()))
|
||||
: block.y + block.getHeight(this.nodesCustomDataShowed());
|
||||
}
|
||||
if (blockHasSelection) {
|
||||
hasSelection = true;
|
||||
@ -659,6 +721,28 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
this.blockSelectionHandler.select(selectedBlocks, true);
|
||||
}
|
||||
|
||||
private nodesCustomDataShowed(): boolean {
|
||||
const storageKey = this.customDataStorageKey();
|
||||
const selectedCustomData = storageGetItem(storageKey, null, false);
|
||||
if (selectedCustomData == null) return false;
|
||||
|
||||
return this.graph.hasCustomData(selectedCustomData, DataTarget.Nodes) &&
|
||||
this.state.showCustomData;
|
||||
}
|
||||
|
||||
private customDataStorageKey(): string {
|
||||
return `${this.phaseName}-selected-custom-data`;
|
||||
}
|
||||
|
||||
private getReadableString(str: string, maxWidth: number): string {
|
||||
if (!str) return "";
|
||||
const strBox = measureText(str);
|
||||
if (maxWidth > strBox.width) return str;
|
||||
const widthOfOneSymbol = Math.floor(strBox.width / str.length);
|
||||
const lengthOfReadableProperties = Math.floor(maxWidth / widthOfOneSymbol);
|
||||
return `${str.slice(0, lengthOfReadableProperties - 3)}..`;
|
||||
}
|
||||
|
||||
// Actions (handlers of toolbox menu and hotkeys events)
|
||||
private layoutAction(view: TurboshaftGraphView): void {
|
||||
view.updateGraphStateType(GraphStateType.NeedToFullRebuild);
|
||||
@ -681,7 +765,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
block.compressHeight();
|
||||
}
|
||||
|
||||
const ranksMaxBlockHeight = view.graph.getRanksMaxBlockHeight(view.state.showProperties);
|
||||
const ranksMaxBlockHeight = view.graph.getRanksMaxBlockHeight(view.nodesCustomDataShowed());
|
||||
|
||||
for (const block of view.graph.blocks()) {
|
||||
block.y = ranksMaxBlockHeight.slice(1, block.rank).reduce<number>((accumulator, current) => {
|
||||
@ -707,15 +791,15 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
view.focusOnSvg();
|
||||
}
|
||||
|
||||
private togglePropertiesAction(view: TurboshaftGraphView): void {
|
||||
view.state.showProperties = !view.state.showProperties;
|
||||
private toggleCustomDataAction(view: TurboshaftGraphView): void {
|
||||
view.state.showCustomData = !view.state.showCustomData;
|
||||
const ranksMaxBlockHeight = new Array<number>();
|
||||
|
||||
for (const block of view.graph.blocks()) {
|
||||
ranksMaxBlockHeight[block.rank] = Math.max(ranksMaxBlockHeight[block.rank] ?? 0,
|
||||
block.collapsed
|
||||
? block.height
|
||||
: block.getHeight(view.state.showProperties));
|
||||
: block.getHeight(view.nodesCustomDataShowed()));
|
||||
}
|
||||
|
||||
for (const block of view.graph.blocks()) {
|
||||
@ -724,8 +808,10 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
}, block.getRankIndent());
|
||||
}
|
||||
|
||||
const element = document.getElementById("toggle-properties");
|
||||
element.classList.toggle("button-input-toggled", view.state.showProperties);
|
||||
const element = document.getElementById("toggle-custom-data");
|
||||
element.classList.toggle("button-input-toggled", view.state.showCustomData);
|
||||
const extent = view.graph.redetermineGraphBoundingBox(view.state.showCustomData);
|
||||
view.panZoom.translateExtent(extent);
|
||||
view.adaptiveUpdateGraphVisibility();
|
||||
}
|
||||
|
||||
@ -767,7 +853,8 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
|
||||
private copyToClipboardHoveredNodeInfo(): void {
|
||||
const node = this.graph.nodeMap[this.hoveredNodeIdentifier];
|
||||
if (!node) return;
|
||||
copyToClipboard(node.getTitle());
|
||||
const customData = this.graph.customData;
|
||||
copyToClipboard(`${node.getTitle()}${customData.getTitle(node.id, DataTarget.Nodes)}`);
|
||||
}
|
||||
|
||||
private selectNodesOfSelectedBlocks(): void {
|
||||
|
@ -29,6 +29,7 @@
|
||||
"src/phases/phase.ts",
|
||||
"src/phases/schedule-phase.ts",
|
||||
"src/phases/sequence-phase.ts",
|
||||
"src/phases/turboshaft-custom-data-phase.ts",
|
||||
"src/selection/selection-storage.ts",
|
||||
"src/selection/selection-map.ts",
|
||||
"src/selection/selection-broker.ts",
|
||||
|
Loading…
Reference in New Issue
Block a user