mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-03 21:08:18 +00:00
Add graph type error diagnostics to the UI (#1535)
* Fontend input types * Fix index of errors / types * Bug fixes, styling improvements, and code review * Improvements to the error box --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
96b5d7b520
commit
947a131a4b
20 changed files with 566 additions and 170 deletions
|
@ -101,22 +101,19 @@
|
|||
--color-e-nearwhite-rgb: 238, 238, 238;
|
||||
--color-f-white: #fff;
|
||||
--color-f-white-rgb: 255, 255, 255;
|
||||
--color-error-red: #d6536e;
|
||||
--color-error-red-rgb: 214, 83, 110;
|
||||
|
||||
--color-data-general: #c5c5c5;
|
||||
--color-data-general-dim: #767676;
|
||||
--color-data-vector: #65bbe5;
|
||||
--color-data-vector-dim: #4b778c;
|
||||
--color-data-number: #cbbab4;
|
||||
--color-data-number-dim: #87736b;
|
||||
--color-data-raster: #e4bb72;
|
||||
--color-data-raster-dim: #8b7752;
|
||||
--color-data-mask: #8d85c7;
|
||||
--color-data-number: #d6536e;
|
||||
--color-data-number-dim: #803242;
|
||||
--color-data-vec2: #cc00ff;
|
||||
--color-data-vec2-dim: #71008d;
|
||||
--color-data-color: #70a898;
|
||||
--color-data-color-dim: #43645b;
|
||||
--color-data-graphic: #e4bb72;
|
||||
--color-data-graphic-dim: #8b7752;
|
||||
--color-data-vector: #65bbe5;
|
||||
--color-data-vector-dim: #4b778c;
|
||||
--color-data-color: #dce472;
|
||||
--color-data-color-dim: #898d55;
|
||||
--color-data-artboard: #70a898;
|
||||
--color-data-artboard-dim: #3a6156;
|
||||
|
||||
|
|
|
@ -627,7 +627,6 @@
|
|||
width: 24px;
|
||||
font-size: 0;
|
||||
overflow: hidden;
|
||||
transition: background-color 0.5s ease;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
|
@ -636,6 +635,7 @@
|
|||
// For the least jarring luminance conversion, these colors are derived by placing a black layer with the "desaturate" blend mode over the colors.
|
||||
// We don't use the CSS `filter: grayscale(1);` property because it produces overly dark tones for bright colors with a noticeable jump on hover.
|
||||
background: var(--pure-color-gray);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover div,
|
||||
|
|
|
@ -583,7 +583,7 @@
|
|||
|
||||
&[title^="Coming Soon"] {
|
||||
opacity: 0.25;
|
||||
transition: opacity 0.25s;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
|
@ -703,7 +703,7 @@
|
|||
|
||||
.graph-view {
|
||||
pointer-events: none;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
opacity: 0;
|
||||
|
||||
&.open {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { getContext, onMount, tick } from "svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
import { FADE_TRANSITION } from "@graphite/consts";
|
||||
import type { NodeGraphState } from "@graphite/state-providers/node-graph";
|
||||
import type { IconName } from "@graphite/utility-functions/icons";
|
||||
import type { Editor } from "@graphite/wasm-communication/editor";
|
||||
import { UpdateNodeGraphSelection } from "@graphite/wasm-communication/messages";
|
||||
import type { FrontendNodeLink, FrontendNodeType, FrontendNode, FrontendGraphDataType } from "@graphite/wasm-communication/messages";
|
||||
import type { FrontendNodeLink, FrontendNodeType, FrontendNode, FrontendGraphInput, FrontendGraphOutput } from "@graphite/wasm-communication/messages";
|
||||
|
||||
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
|
||||
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
|
||||
|
@ -28,7 +30,7 @@
|
|||
let nodesContainer: HTMLDivElement | undefined;
|
||||
let nodeSearchInput: TextInput | undefined;
|
||||
|
||||
let transform = { scale: 1, x: 0, y: 0 };
|
||||
let transform = { scale: 1, x: 1200, y: 0 };
|
||||
let panning = false;
|
||||
let selected: bigint[] = [];
|
||||
let draggingNodes: { startX: number; startY: number; roundX: number; roundY: number } | undefined = undefined;
|
||||
|
@ -292,6 +294,8 @@
|
|||
function pointerDown(e: PointerEvent) {
|
||||
const [lmb, rmb] = [e.button === 0, e.button === 2];
|
||||
|
||||
const nodeError = (e.target as SVGSVGElement).closest("[data-node-error]") as HTMLElement;
|
||||
if (nodeError && lmb) return;
|
||||
const port = (e.target as SVGSVGElement).closest("[data-port]") as SVGSVGElement;
|
||||
const node = (e.target as HTMLElement).closest("[data-node]") as HTMLElement | undefined;
|
||||
const nodeId = node?.getAttribute("data-node") || undefined;
|
||||
|
@ -606,9 +610,9 @@
|
|||
return `M-2,-2 L${nodeWidth + 2},-2 L${nodeWidth + 2},${nodeHeight + 2} L-2,${nodeHeight + 2}z ${rectangles.join(" ")}`;
|
||||
}
|
||||
|
||||
function dataTypeTooltip(dataType: FrontendGraphDataType): string {
|
||||
const capitalized = dataType[0].toUpperCase() + dataType.slice(1);
|
||||
return `${capitalized} Data`;
|
||||
function dataTypeTooltip(value: FrontendGraphInput | FrontendGraphOutput): string {
|
||||
const dataTypeCapitalized = `${value.dataType[0].toUpperCase()}${value.dataType.slice(1)}`;
|
||||
return value.resolvedType ? `Resolved Data: ${value.resolvedType}` : `Unresolved Data: ${dataTypeCapitalized}`;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
@ -674,7 +678,7 @@
|
|||
<!-- Layers -->
|
||||
{#each $nodeGraph.nodes.flatMap((node, nodeIndex) => (node.isLayer ? [{ node, nodeIndex }] : [])) as { node, nodeIndex } (nodeIndex)}
|
||||
{@const clipPathId = String(Math.random()).substring(2)}
|
||||
{@const stackDatainput = node.exposedInputs[0]}
|
||||
{@const stackDataInput = node.exposedInputs[0]}
|
||||
<div
|
||||
class="layer"
|
||||
class:selected={selected.includes(node.id)}
|
||||
|
@ -687,6 +691,10 @@
|
|||
style:--data-color-dim={`var(--color-data-${node.primaryOutput?.dataType || "general"}-dim)`}
|
||||
data-node={node.id}
|
||||
>
|
||||
{#if node.errors}
|
||||
<span class="node-error faded" transition:fade={FADE_TRANSITION} data-node-error>{node.errors}</span>
|
||||
<span class="node-error hover" transition:fade={FADE_TRANSITION} data-node-error>{node.errors}</span>
|
||||
{/if}
|
||||
<div class="node-chain" />
|
||||
<!-- Layer input port (from left) -->
|
||||
<div class="input ports">
|
||||
|
@ -701,7 +709,7 @@
|
|||
bind:this={inputs[nodeIndex][0]}
|
||||
>
|
||||
{#if node.primaryInput}
|
||||
<title>{dataTypeTooltip(node.primaryInput.dataType)}</title>
|
||||
<title>{dataTypeTooltip(node.primaryInput)}</title>
|
||||
{/if}
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" />
|
||||
</svg>
|
||||
|
@ -721,7 +729,7 @@
|
|||
style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType}-dim)`}
|
||||
bind:this={outputs[nodeIndex][0]}
|
||||
>
|
||||
<title>{dataTypeTooltip(node.primaryOutput.dataType)}</title>
|
||||
<title>{dataTypeTooltip(node.primaryOutput)}</title>
|
||||
<path d="M0,2.953,2.521,1.259a2.649,2.649,0,0,1,2.959,0L8,2.953V8H0Z" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -730,12 +738,12 @@
|
|||
viewBox="0 0 8 8"
|
||||
class="port bottom"
|
||||
data-port="input"
|
||||
data-datatype={stackDatainput.dataType}
|
||||
style:--data-color={`var(--color-data-${stackDatainput.dataType})`}
|
||||
style:--data-color-dim={`var(--color-data-${stackDatainput.dataType}-dim)`}
|
||||
data-datatype={stackDataInput.dataType}
|
||||
style:--data-color={`var(--color-data-${stackDataInput.dataType})`}
|
||||
style:--data-color-dim={`var(--color-data-${stackDataInput.dataType}-dim)`}
|
||||
bind:this={inputs[nodeIndex][1]}
|
||||
>
|
||||
<title>{dataTypeTooltip(stackDatainput.dataType)}</title>
|
||||
<title>{dataTypeTooltip(stackDataInput)}</title>
|
||||
<path d="M0,0H8V8L5.479,6.319a2.666,2.666,0,0,0-2.959,0L0,8Z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
@ -769,6 +777,10 @@
|
|||
style:--data-color-dim={`var(--color-data-${node.primaryOutput?.dataType || "general"}-dim)`}
|
||||
data-node={node.id}
|
||||
>
|
||||
{#if node.errors}
|
||||
<span class="node-error faded" transition:fade={FADE_TRANSITION} data-node-error>{node.errors}</span>
|
||||
<span class="node-error hover" transition:fade={FADE_TRANSITION} data-node-error>{node.errors}</span>
|
||||
{/if}
|
||||
<!-- Primary row -->
|
||||
<div class="primary" class:no-parameter-section={exposedInputsOutputs.length === 0}>
|
||||
<IconLabel icon={nodeIcon(node.name)} />
|
||||
|
@ -798,7 +810,7 @@
|
|||
style:--data-color-dim={`var(--color-data-${node.primaryInput?.dataType}-dim)`}
|
||||
bind:this={inputs[nodeIndex][0]}
|
||||
>
|
||||
<title>{dataTypeTooltip(node.primaryInput.dataType)}</title>
|
||||
<title>{dataTypeTooltip(node.primaryInput)}</title>
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -814,7 +826,7 @@
|
|||
style:--data-color-dim={`var(--color-data-${parameter.dataType}-dim)`}
|
||||
bind:this={inputs[nodeIndex][index + 1]}
|
||||
>
|
||||
<title>{dataTypeTooltip(parameter.dataType)}</title>
|
||||
<title>{dataTypeTooltip(parameter)}</title>
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -833,7 +845,7 @@
|
|||
style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType}-dim)`}
|
||||
bind:this={outputs[nodeIndex][0]}
|
||||
>
|
||||
<title>{dataTypeTooltip(node.primaryOutput.dataType)}</title>
|
||||
<title>{dataTypeTooltip(node.primaryOutput)}</title>
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" />
|
||||
</svg>
|
||||
{/if}
|
||||
|
@ -846,9 +858,9 @@
|
|||
data-datatype={parameter.dataType}
|
||||
style:--data-color={`var(--color-data-${parameter.dataType})`}
|
||||
style:--data-color-dim={`var(--color-data-${parameter.dataType}-dim)`}
|
||||
bind:this={outputs[nodeIndex][outputIndex + 1]}
|
||||
bind:this={outputs[nodeIndex][outputIndex + (node.primaryOutput ? 1 : 0)]}
|
||||
>
|
||||
<title>{dataTypeTooltip(parameter.dataType)}</title>
|
||||
<title>{dataTypeTooltip(parameter)}</title>
|
||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" />
|
||||
</svg>
|
||||
{/each}
|
||||
|
@ -980,6 +992,68 @@
|
|||
// backdrop-filter: blur(4px);
|
||||
background: rgba(0, 0, 0, 0.33);
|
||||
|
||||
.node-error {
|
||||
position: absolute;
|
||||
width: max-content;
|
||||
white-space: pre-wrap;
|
||||
max-width: 600px;
|
||||
line-height: 18px;
|
||||
color: var(--color-2-mildblack);
|
||||
background: var(--color-error-red);
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
bottom: calc(100% + 12px);
|
||||
z-index: -1;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
opacity: 0.5;
|
||||
|
||||
// Tail
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
bottom: -8px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 8px 6px 0 6px;
|
||||
border-color: var(--color-error-red) transparent transparent transparent;
|
||||
}
|
||||
|
||||
&.hover {
|
||||
opacity: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.faded:hover + .hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.faded:hover {
|
||||
z-index: 2;
|
||||
opacity: 1;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
transition:
|
||||
opacity 0.2s ease-in-out,
|
||||
z-index 0s 0.2s;
|
||||
|
||||
&::selection {
|
||||
background-color: var(--color-e-nearwhite);
|
||||
|
||||
// Target only Safari
|
||||
@supports (background: -webkit-named-image(i)) {
|
||||
& {
|
||||
// Setting an alpha value opts out of Safari's "fancy" (but not visible on dark backgrounds) selection highlight rendering
|
||||
// https://stackoverflow.com/a/71753552/775283
|
||||
background-color: rgba(var(--color-e-nearwhite-rgb), calc(254 / 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</script>
|
||||
|
||||
<LayoutRow class="parameter-expose-button">
|
||||
<button class:exposed style:--data-type-color={`var(--color-data-${dataType})`} on:click={action} title={tooltip} tabindex="-1">
|
||||
<button class:exposed style:--data-type-color={`var(--color-data-${dataType})`} style:--data-type-color-dim={`var(--color-data-${dataType}-dim)`} on:click={action} title={tooltip} tabindex="-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
|
||||
<path class="interior" d="M0,7.882c0,1.832,1.325,2.63,2.945,1.772L8.785,6.56c1.62-.858,1.62-2.262,0-3.12L2.945.345C1.325-.512,0,.285,0,2.118Z" />
|
||||
<path
|
||||
|
@ -53,25 +53,33 @@
|
|||
}
|
||||
|
||||
&:not(.exposed) {
|
||||
.outline {
|
||||
fill: var(--data-type-color);
|
||||
&:not(:hover) {
|
||||
.outline {
|
||||
fill: var(--data-type-color-dim);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.interior {
|
||||
fill: var(--color-6-lowergray);
|
||||
.outline {
|
||||
fill: var(--data-type-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.exposed {
|
||||
.interior {
|
||||
fill: var(--data-type-color);
|
||||
&:not(:hover) {
|
||||
.interior {
|
||||
fill: var(--data-type-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.outline {
|
||||
fill: var(--color-f-white);
|
||||
fill: var(--data-type-color);
|
||||
}
|
||||
|
||||
.interior {
|
||||
fill: var(--data-type-color-dim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
frontend/src/consts.ts
Normal file
2
frontend/src/consts.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
import { cubicInOut } from "svelte/easing";
|
||||
export const FADE_TRANSITION = { duration: 200, easing: cubicInOut };
|
|
@ -82,18 +82,22 @@ export class FrontendDocumentDetails extends DocumentDetails {
|
|||
readonly id!: bigint;
|
||||
}
|
||||
|
||||
export type FrontendGraphDataType = "general" | "raster" | "color" | "vector" | "vec2" | "graphic" | "artboard";
|
||||
export type FrontendGraphDataType = "general" | "number" | "raster" | "vector" | "color" | "artboard";
|
||||
|
||||
export class FrontendGraphInput {
|
||||
readonly dataType!: FrontendGraphDataType;
|
||||
|
||||
readonly name!: string;
|
||||
|
||||
readonly resolvedType!: string | undefined;
|
||||
}
|
||||
|
||||
export class FrontendGraphOutput {
|
||||
readonly dataType!: FrontendGraphDataType;
|
||||
|
||||
readonly name!: string;
|
||||
|
||||
readonly resolvedType!: string | undefined;
|
||||
}
|
||||
|
||||
export class FrontendNode {
|
||||
|
@ -119,6 +123,8 @@ export class FrontendNode {
|
|||
readonly previewed!: boolean;
|
||||
|
||||
readonly disabled!: boolean;
|
||||
|
||||
readonly errors!: string | undefined;
|
||||
}
|
||||
|
||||
export class FrontendNodeLink {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue