WIP: Thumbnails

This commit is contained in:
Adam
2025-07-16 01:43:00 -07:00
parent 8b665d158c
commit fcd379a62f
50 changed files with 1208 additions and 1053 deletions
+8 -2
View File
@@ -92,6 +92,12 @@
updateLayerInTree(targetId, targetLayer);
});
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerDetails, (updateDocumentLayerDetails) => {
const targetLayer = updateDocumentLayerDetails.data;
const targetId = targetLayer.id;
updateLayerInTree(targetId, targetLayer);
});
addEventListener("pointermove", clippingHover);
addEventListener("keydown", clippingKeyPress);
addEventListener("keyup", clippingKeyPress);
@@ -540,8 +546,8 @@
<IconLabel icon="Clipped" class="clipped-arrow" tooltip={"Clipping mask is active (Alt-click border to release)"} />
{/if}
<div class="thumbnail">
{#if $nodeGraph.thumbnails.has(listing.entry.id)}
{@html $nodeGraph.thumbnails.get(listing.entry.id)}
{#if $nodeGraph.thumbnails.get($nodeGraph.wires.get(listing.entry.id)?.get(BigInt(1))?.sni) !== undefined}
{@html $nodeGraph.thumbnails.get($nodeGraph.wires.get(listing.entry.id)?.get(BigInt(1))?.sni)}
{/if}
</div>
{#if listing.entry.name === "Artboard"}
+59 -14
View File
@@ -230,6 +230,22 @@
}
return result;
}
function generateCheckerboardString() {
let rects = "";
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 8; x++) {
const isDark = (x + y) % 2 === 0;
const fill = isDark ? "#cccccc" : "#ffffff";
rects += `<rect x="${x * 4 - 16}" y="${y * 4 - 12}" width="${4.1}" height="${4.1}" fill="${fill}" />`;
}
}
rects += ``;
return rects;
}
</script>
<div
@@ -317,10 +333,11 @@
<div class="wires" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
<svg>
{#each $nodeGraph.wires.values() as map}
{#each map.values() as { pathString, dataType, thick, dashed }}
{#each map.values() as { pathString, dataType, thick, dashed, center, sni }}
{#if thick}
<path
d={pathString}
class="wire-path"
style:--data-line-width={"8px"}
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
@@ -513,8 +530,8 @@
<span class="node-error hover" transition:fade={FADE_TRANSITION} title="" data-node-error>{node.errors}</span>
{/if}
<div class="thumbnail">
{#if $nodeGraph.thumbnails.has(node.id)}
{@html $nodeGraph.thumbnails.get(node.id)}
{#if node.layerThumbnailSNI && $nodeGraph.thumbnails.has(node.layerThumbnailSNI)}
{@html $nodeGraph.thumbnails.get(node.layerThumbnailSNI)}
{/if}
<!-- Layer stacking top output -->
{#if node.primaryOutput}
@@ -613,8 +630,8 @@
<div class="wires">
<svg>
{#each $nodeGraph.wires.values() as map}
{#each map.values() as { pathString, dataType, thick, dashed, center, monitorSni }}
{#if !thick}
{#each map.values() as { pathString, dataType, thick, dashed, center, sni }}
{#if !thick && pathString}
<path
d={pathString}
class="wire-path"
@@ -623,20 +640,16 @@
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
/>
{/if}
<!-- {console.log("center: ", center)} -->
<!-- {console.log("monitorSni: ", monitorSni)} -->
{#if center && monitorSni}
<!-- {console.log("thumbnails: ", $nodeGraph.thumbnails.get(monitorSni))} -->
<g transform={`translate(${center[0]}, ${center[1]})`}>
{@html $nodeGraph.thumbnails.get(monitorSni)}
</g>
<!-- <g transform={`translate(${center[0] - 30}, ${center[1] - 10})`}>
<text fill="white">{sni}</text>
</g> -->
{/if}
{/each}
{/each}
{#if $nodeGraph.wirePathInProgress}
<path
d={$nodeGraph.wirePathInProgress?.pathString}
class="wire-path"
style:--data-line-width={`${$nodeGraph.wirePathInProgress.thick ? 8 : 2}px`}
style:--data-color={`var(--color-data-${$nodeGraph.wirePathInProgress.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${$nodeGraph.wirePathInProgress.dataType.toLowerCase()}-dim)`}
@@ -644,6 +657,22 @@
/>
{/if}
</svg>
{#each $nodeGraph.wires.values() as map}
{#each map.values() as { pathString, center, sni, dataType, thick }}
{#if !thick && pathString && sni && center !== undefined}
<div
class="wire-thumbnail"
style:--offset-left-px={`${center[0] - 12}px`}
style:--offset-top-px={`${center[1] - 8}px`}
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
>
{#if sni && $nodeGraph.thumbnails.has(sni)}
{@html $nodeGraph.thumbnails.get(sni)}
{/if}
</div>
{/if}
{/each}
{/each}
</div>
<!-- Nodes -->
@@ -891,7 +920,6 @@
position: absolute;
width: 100%;
height: 100%;
svg {
width: 100%;
height: 100%;
@@ -904,6 +932,23 @@
stroke-dasharray: var(--data-dasharray);
}
}
.wire-thumbnail {
// background: var(--color-2-mildblack);
border: 1px solid var(--data-color-dim);
border-radius: 2px;
position: absolute;
left: var(--offset-left-px);
top: var(--offset-top-px);
box-sizing: border-box;
width: 24px;
height: 16px;
// background-color: white;
background-image: var(--color-transparent-checkered-background);
background-size: var(--color-transparent-checkered-background-size-mini);
background-position: var(--color-transparent-checkered-background-position-mini);
background-repeat: var(--color-transparent-checkered-background-repeat);
}
}
.imports-and-exports {
+34 -6
View File
@@ -12,6 +12,7 @@ export class JsMessage {
}
const TupleToVec2 = Transform(({ value }: { value: [number, number] | undefined }) => (value === undefined ? undefined : { x: value[0], y: value[1] }));
const ImportsToVec2Array = Transform(({ obj: { imports } }: { obj: { imports: [FrontendGraphOutput, number, number][] } }) =>
imports.map(([outputMetadata, x, y]) => ({ outputMetadata, position: { x, y } })),
);
@@ -36,6 +37,10 @@ export class UpdateBox extends JsMessage {
readonly box!: Box | undefined;
}
export class UpdateGraphBreadcrumbPath extends JsMessage {
readonly breadcrumbPath!: bigint[];
}
export class UpdateClickTargets extends JsMessage {
readonly clickTargets!: FrontendClickTargets | undefined;
}
@@ -57,6 +62,10 @@ export class UpdateContextMenuInformation extends JsMessage {
readonly contextMenuInformation!: ContextMenuInformation | undefined;
}
export class UpdateContextDuringEvaluation extends JsMessage {
readonly contextDuringEvaluation!: [bigint, number, string][];
}
export class UpdateImportsExports extends JsMessage {
@ImportsToVec2Array
readonly imports!: { outputMetadata: FrontendGraphOutput; position: XY }[];
@@ -109,7 +118,7 @@ export class UpdateNodeGraphWires extends JsMessage {
readonly wires!: WireUpdate[];
}
export class ClearAllNodeGraphWires extends JsMessage {}
export class ClearAllNodeGraphWirePaths extends JsMessage {}
export class UpdateNodeGraphTransform extends JsMessage {
readonly transform!: NodeGraphTransform;
@@ -128,6 +137,10 @@ export class UpdateThumbnails extends JsMessage {
readonly add!: [bigint, string][];
readonly clear!: bigint[];
readonly wireSNIUpdates!: WireSNIUpdate[];
readonly layerSNIUpdates!: FrontendLayerSNIUpdate[];
}
export class UpdateNodeGraphSelection extends JsMessage {
@@ -266,6 +279,8 @@ export class FrontendGraphOutput {
export class FrontendNode {
readonly isLayer!: boolean;
layerThumbnailSNI!: bigint | undefined;
readonly canBeLayer!: boolean;
readonly id!: bigint;
@@ -302,6 +317,12 @@ export class FrontendNode {
readonly uiOnly!: boolean;
}
export class FrontendLayerSNIUpdate {
readonly id!: bigint;
readonly sni!: bigint | undefined;
}
export class FrontendNodeType {
readonly name!: string;
@@ -317,13 +338,12 @@ export class NodeGraphTransform {
}
export class WirePath {
readonly pathString!: string;
pathString!: string;
readonly dataType!: FrontendGraphDataType;
readonly thick!: boolean;
readonly dashed!: boolean;
@TupleToVec2
readonly center!: XY | undefined;
readonly inputSni!: bigint;
readonly center!: [number, number] | undefined;
sni!: bigint | undefined;
}
export class WireUpdate {
@@ -332,6 +352,12 @@ export class WireUpdate {
readonly wirePathUpdate!: WirePath | undefined;
}
export class WireSNIUpdate {
readonly id!: bigint;
readonly inputIndex!: number;
readonly sni!: bigint | undefined;
}
export class IndexedDbDocumentDetails extends DocumentDetails {
@Transform(({ value }: { value: bigint }) => value.toString())
id!: string;
@@ -1624,7 +1650,7 @@ type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHand
type MessageMaker = typeof JsMessage | JSMessageFactory;
export const messageMakers: Record<string, MessageMaker> = {
ClearAllNodeGraphWires,
ClearAllNodeGraphWirePaths,
DisplayDialog,
DisplayDialogDismiss,
DisplayDialogPanic,
@@ -1653,8 +1679,10 @@ export const messageMakers: Record<string, MessageMaker> = {
TriggerVisitLink,
UpdateActiveDocument,
UpdateBox,
UpdateGraphBreadcrumbPath,
UpdateClickTargets,
UpdateContextMenuInformation,
UpdateContextDuringEvaluation,
UpdateDialogButtons,
UpdateDialogColumn1,
UpdateDialogColumn2,
+55 -8
View File
@@ -9,11 +9,13 @@ import {
type FrontendNode,
type FrontendNodeType,
type WirePath,
ClearAllNodeGraphWires,
ClearAllNodeGraphWirePaths,
SendUIMetadata,
UpdateBox,
UpdateGraphBreadcrumbPath,
UpdateClickTargets,
UpdateContextMenuInformation,
UpdateContextDuringEvaluation,
UpdateInSelectedNetwork,
UpdateImportReorderIndex,
UpdateExportReorderIndex,
@@ -32,6 +34,7 @@ import {
export function createNodeGraphState(editor: Editor) {
const { subscribe, update } = writable({
box: undefined as Box | undefined,
breadcrumbPath: [] as bigint[],
clickTargets: undefined as FrontendClickTargets | undefined,
contextMenuInformation: undefined as ContextMenuInformation | undefined,
layerWidths: new Map<bigint, number>(),
@@ -43,8 +46,9 @@ export function createNodeGraphState(editor: Editor) {
addExport: undefined as { x: number; y: number } | undefined,
nodes: new Map<bigint, FrontendNode>(),
visibleNodes: new Set<bigint>(),
/// The index is the exposed input index. The exports have a first key value of u32::MAX.
/// The first key is the document node id. The index is the actual input index. The exports have a first key value of u32::MAX.
wires: new Map<bigint, Map<number, WirePath>>(),
/// The first key is the caller stable node id
wirePathInProgress: undefined as WirePath | undefined,
nodeDescriptions: new Map<string, string>(),
nodeTypes: [] as FrontendNodeType[],
@@ -70,6 +74,13 @@ export function createNodeGraphState(editor: Editor) {
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateGraphBreadcrumbPath, (updateGraphBreadcrumbPath) => {
update((state) => {
state.breadcrumbPath = updateGraphBreadcrumbPath.breadcrumbPath;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (UpdateClickTargets) => {
update((state) => {
state.clickTargets = UpdateClickTargets.clickTargets;
@@ -82,6 +93,11 @@ export function createNodeGraphState(editor: Editor) {
return state;
});
});
// editor.subscriptions.subscribeJsMessage(UpdateContextDuringEvaluation, (updateContextDuringEvaluation) => {
// update((state) => {
// return state;
// });
// });
editor.subscriptions.subscribeJsMessage(UpdateImportReorderIndex, (updateImportReorderIndex) => {
update((state) => {
state.reorderImportIndex = updateImportReorderIndex.importIndex;
@@ -118,7 +134,6 @@ export function createNodeGraphState(editor: Editor) {
});
});
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => {
// console.log(updateNodeGraphNodes);
update((state) => {
state.nodes.clear();
updateNodeGraphNodes.nodes.forEach((node) => {
@@ -127,6 +142,7 @@ export function createNodeGraphState(editor: Editor) {
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateVisibleNodes, (updateVisibleNodes) => {
update((state) => {
state.visibleNodes = new Set<bigint>(updateVisibleNodes.nodes);
@@ -143,17 +159,33 @@ export function createNodeGraphState(editor: Editor) {
state.wires.set(wireUpdate.id, inputMap);
}
if (wireUpdate.wirePathUpdate !== undefined) {
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
const existing = inputMap.get(wireUpdate.inputIndex);
if (existing) {
inputMap.set(wireUpdate.inputIndex, {
...wireUpdate.wirePathUpdate,
sni: existing.sni,
});
} else {
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
}
} else {
inputMap.delete(wireUpdate.inputIndex);
const existing = inputMap.get(wireUpdate.inputIndex);
if (existing) {
existing.pathString = "";
}
}
});
return state;
});
});
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWires, (_) => {
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWirePaths, (_) => {
update((state) => {
state.wires.clear();
for (const [, innerMap] of state.wires) {
for (const [, wirePath] of innerMap) {
wirePath.pathString = "";
}
}
return state;
});
});
@@ -178,7 +210,22 @@ export function createNodeGraphState(editor: Editor) {
for (const id of updateThumbnails.clear) {
state.thumbnails.set(id, "");
}
// console.log("thumbnails: ", state.thumbnails);
updateThumbnails.wireSNIUpdates.forEach((wireUpdate) => {
const inputMap = state.wires.get(wireUpdate.id);
if (inputMap) {
const wire = inputMap.get(wireUpdate.inputIndex);
if (wire) {
wire.sni = wireUpdate.sni;
}
}
});
updateThumbnails.layerSNIUpdates.forEach((wireUpdate) => {
const node = state.nodes.get(wireUpdate.id);
if (node) {
node.layerThumbnailSNI = wireUpdate.sni;
}
});
return state;
});
});