@bian-womp/spark-workbench 0.2.90 → 0.2.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/index.cjs +114 -66
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts +1 -2
- package/lib/cjs/src/misc/DefaultNodeHeader.d.ts.map +1 -1
- package/lib/cjs/src/misc/thumbnail-utils.d.ts +34 -2
- package/lib/cjs/src/misc/thumbnail-utils.d.ts.map +1 -1
- package/lib/esm/index.js +114 -66
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts +1 -2
- package/lib/esm/src/misc/DefaultNodeHeader.d.ts.map +1 -1
- package/lib/esm/src/misc/thumbnail-utils.d.ts +34 -2
- package/lib/esm/src/misc/thumbnail-utils.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
export declare function DefaultNodeHeader({ id, typeId,
|
|
2
|
+
export declare function DefaultNodeHeader({ id, typeId, validation, right, showId, onInvalidate, }: {
|
|
3
3
|
id: string;
|
|
4
4
|
typeId?: string;
|
|
5
|
-
title?: string;
|
|
6
5
|
validation: {
|
|
7
6
|
inputs?: any[];
|
|
8
7
|
outputs?: any[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultNodeHeader.d.ts","sourceRoot":"","sources":["../../../../src/misc/DefaultNodeHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,MAAM,EACN,
|
|
1
|
+
{"version":3,"file":"DefaultNodeHeader.d.ts","sourceRoot":"","sources":["../../../../src/misc/DefaultNodeHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,MAAM,EACN,UAAU,EACV,KAAK,EACL,MAAM,EACN,YAAY,GACb,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QAAE,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC;IAChE,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B,2CAgJA"}
|
|
@@ -2,16 +2,48 @@
|
|
|
2
2
|
* Flow thumbnail capture utility
|
|
3
3
|
* Captures React Flow canvas as SVG image
|
|
4
4
|
*/
|
|
5
|
+
export interface ThumbnailOptions {
|
|
6
|
+
/**
|
|
7
|
+
* If true, export the whole graph instead of just the viewport. Default: true
|
|
8
|
+
*/
|
|
9
|
+
ignoreViewport?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Padding to add when exporting whole graph (in pixels). Default: 40
|
|
12
|
+
*/
|
|
13
|
+
padding?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Whether to export handles. Default: true
|
|
16
|
+
*/
|
|
17
|
+
exportHandles?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Whether to export node titles. Default: true
|
|
20
|
+
*/
|
|
21
|
+
exportNodeTitle?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether to export background dot pattern. Default: true
|
|
24
|
+
*/
|
|
25
|
+
exportBackgroundPattern?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Background color of nodes. Default: "#f3f4f6"
|
|
28
|
+
*/
|
|
29
|
+
nodeBackgroundColor?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Color of background dot pattern. Default: "#f1f1f1"
|
|
32
|
+
*/
|
|
33
|
+
backgroundPatternColor?: string;
|
|
34
|
+
}
|
|
5
35
|
/**
|
|
6
36
|
* Captures a React Flow container element as an SVG image and returns data URL
|
|
7
37
|
* @param containerElement - The React Flow container DOM element
|
|
38
|
+
* @param options - Options for thumbnail capture
|
|
8
39
|
* @returns Promise resolving to SVG data URL string, or null if capture fails
|
|
9
40
|
*/
|
|
10
|
-
export declare function captureCanvasThumbnail(containerElement: HTMLElement | null): Promise<string | null>;
|
|
41
|
+
export declare function captureCanvasThumbnail(containerElement: HTMLElement | null, options?: ThumbnailOptions): Promise<string | null>;
|
|
11
42
|
/**
|
|
12
43
|
* Captures a React Flow container element as an SVG image and downloads it
|
|
13
44
|
* @param containerElement - The React Flow container DOM element
|
|
45
|
+
* @param options - Options for thumbnail capture
|
|
14
46
|
* @returns Promise resolving to true if successful, false otherwise
|
|
15
47
|
*/
|
|
16
|
-
export declare function downloadCanvasThumbnail(containerElement: HTMLElement | null): Promise<boolean>;
|
|
48
|
+
export declare function downloadCanvasThumbnail(containerElement: HTMLElement | null, options?: ThumbnailOptions): Promise<boolean>;
|
|
17
49
|
//# sourceMappingURL=thumbnail-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thumbnail-utils.d.ts","sourceRoot":"","sources":["../../../../src/misc/thumbnail-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"thumbnail-utils.d.ts","sourceRoot":"","sources":["../../../../src/misc/thumbnail-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2DH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;OAEG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AA6rBD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,gBAAgB,EAAE,WAAW,GAAG,IAAI,EACpC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwIxB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,gBAAgB,EAAE,WAAW,GAAG,IAAI,EACpC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAoBlB"}
|
package/lib/esm/index.js
CHANGED
|
@@ -3149,11 +3149,12 @@ function extractEdgePaths(viewport, visibleBounds) {
|
|
|
3149
3149
|
if (!pathData)
|
|
3150
3150
|
return;
|
|
3151
3151
|
const pathBounds = getPathBounds(pathData);
|
|
3152
|
-
// Only include edge if it intersects with visible viewport
|
|
3153
|
-
if (
|
|
3154
|
-
pathBounds.
|
|
3155
|
-
|
|
3156
|
-
|
|
3152
|
+
// Only include edge if it intersects with visible viewport (or if ignoring viewport)
|
|
3153
|
+
if (!visibleBounds ||
|
|
3154
|
+
(pathBounds.maxX >= visibleBounds.minX &&
|
|
3155
|
+
pathBounds.minX <= visibleBounds.maxX &&
|
|
3156
|
+
pathBounds.maxY >= visibleBounds.minY &&
|
|
3157
|
+
pathBounds.minY <= visibleBounds.maxY)) {
|
|
3157
3158
|
edges.push({
|
|
3158
3159
|
d: pathData,
|
|
3159
3160
|
stroke: extractStrokeColor(pathEl),
|
|
@@ -3199,10 +3200,10 @@ function extractNodeDimensions(nodeEl) {
|
|
|
3199
3200
|
/**
|
|
3200
3201
|
* Extracts node styles (colors, border, radius) from computed styles
|
|
3201
3202
|
*/
|
|
3202
|
-
function extractNodeStyles(nodeContent) {
|
|
3203
|
+
function extractNodeStyles(nodeContent, nodeBackgroundColor) {
|
|
3203
3204
|
const computedStyle = window.getComputedStyle(nodeContent);
|
|
3204
|
-
// Use gray background for nodes in thumbnail
|
|
3205
|
-
const fill = "#f3f4f6"; // gray-100 equivalent
|
|
3205
|
+
// Use provided background color or default gray background for nodes in thumbnail
|
|
3206
|
+
const fill = nodeBackgroundColor || "#f3f4f6"; // gray-100 equivalent
|
|
3206
3207
|
const stroke = extractStrokeColor(nodeContent);
|
|
3207
3208
|
const strokeWidth = extractStrokeWidth(nodeContent, 1);
|
|
3208
3209
|
const borderRadiusStr = computedStyle.borderRadius || "8px";
|
|
@@ -3309,7 +3310,8 @@ function extractNodeTitle(nodeEl, nodeX, nodeY) {
|
|
|
3309
3310
|
/**
|
|
3310
3311
|
* Extracts visible nodes from React Flow viewport
|
|
3311
3312
|
*/
|
|
3312
|
-
function extractNodes(viewport, visibleBounds) {
|
|
3313
|
+
function extractNodes(viewport, visibleBounds, options = {}) {
|
|
3314
|
+
const { exportHandles = true, exportNodeTitle = true, nodeBackgroundColor, } = options;
|
|
3313
3315
|
const nodes = [];
|
|
3314
3316
|
let minX = Infinity;
|
|
3315
3317
|
let minY = Infinity;
|
|
@@ -3323,11 +3325,16 @@ function extractNodes(viewport, visibleBounds) {
|
|
|
3323
3325
|
const nodeContent = nodeEl.firstElementChild;
|
|
3324
3326
|
if (!nodeContent)
|
|
3325
3327
|
return;
|
|
3326
|
-
const styles = extractNodeStyles(nodeContent);
|
|
3327
|
-
const handles =
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3328
|
+
const styles = extractNodeStyles(nodeContent, nodeBackgroundColor);
|
|
3329
|
+
const handles = exportHandles
|
|
3330
|
+
? extractNodeHandles(nodeEl, position.x, position.y, dimensions.width)
|
|
3331
|
+
: [];
|
|
3332
|
+
const title = exportNodeTitle
|
|
3333
|
+
? extractNodeTitle(nodeEl, position.x, position.y)
|
|
3334
|
+
: undefined;
|
|
3335
|
+
// Only include node if it's within visible viewport (or if ignoring viewport)
|
|
3336
|
+
if (!visibleBounds ||
|
|
3337
|
+
isRectVisible(position.x, position.y, dimensions.width, dimensions.height, visibleBounds)) {
|
|
3331
3338
|
nodes.push({
|
|
3332
3339
|
x: position.x,
|
|
3333
3340
|
y: position.y,
|
|
@@ -3359,43 +3366,47 @@ function extractNodes(viewport, visibleBounds) {
|
|
|
3359
3366
|
/**
|
|
3360
3367
|
* Creates SVG element with dot pattern background (matching React Flow)
|
|
3361
3368
|
*/
|
|
3362
|
-
function createSVGElement(width, height) {
|
|
3369
|
+
function createSVGElement(width, height, options = {}) {
|
|
3370
|
+
const { exportBackgroundPattern = true, backgroundPatternColor = "#f1f1f1" } = options;
|
|
3363
3371
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
3364
3372
|
svg.setAttribute("width", String(width));
|
|
3365
3373
|
svg.setAttribute("height", String(height));
|
|
3366
3374
|
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
3367
|
-
// Create defs section for patterns
|
|
3368
|
-
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
3369
|
-
// Create dot pattern (matching React Flow's BackgroundVariant.Dots)
|
|
3370
|
-
// React Flow uses gap={12} and size={1} by default
|
|
3371
|
-
const pattern = document.createElementNS("http://www.w3.org/2000/svg", "pattern");
|
|
3372
|
-
pattern.setAttribute("id", "dot-pattern");
|
|
3373
|
-
pattern.setAttribute("x", "0");
|
|
3374
|
-
pattern.setAttribute("y", "0");
|
|
3375
|
-
pattern.setAttribute("width", "24"); // gap between dots (matching React Flow default)
|
|
3376
|
-
pattern.setAttribute("height", "24");
|
|
3377
|
-
pattern.setAttribute("patternUnits", "userSpaceOnUse");
|
|
3378
|
-
// Create a circle for the dot (centered in the pattern cell)
|
|
3379
|
-
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
3380
|
-
circle.setAttribute("cx", "12"); // Center of 24x24 pattern cell
|
|
3381
|
-
circle.setAttribute("cy", "12");
|
|
3382
|
-
circle.setAttribute("r", "1"); // dot radius = 1 (matching React Flow size={1})
|
|
3383
|
-
circle.setAttribute("fill", "#f1f1f1"); // gray color matching React Flow default
|
|
3384
|
-
pattern.appendChild(circle);
|
|
3385
|
-
defs.appendChild(pattern);
|
|
3386
|
-
svg.appendChild(defs);
|
|
3387
3375
|
// Create background rectangle with white base
|
|
3388
3376
|
const bgRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
3389
3377
|
bgRect.setAttribute("width", String(width));
|
|
3390
3378
|
bgRect.setAttribute("height", String(height));
|
|
3391
3379
|
bgRect.setAttribute("fill", "#ffffff"); // Base background color
|
|
3392
3380
|
svg.appendChild(bgRect);
|
|
3393
|
-
// Create pattern
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3381
|
+
// Create dot pattern if enabled
|
|
3382
|
+
if (exportBackgroundPattern) {
|
|
3383
|
+
// Create defs section for patterns
|
|
3384
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
3385
|
+
// Create dot pattern (matching React Flow's BackgroundVariant.Dots)
|
|
3386
|
+
// React Flow uses gap={12} and size={1} by default
|
|
3387
|
+
const pattern = document.createElementNS("http://www.w3.org/2000/svg", "pattern");
|
|
3388
|
+
pattern.setAttribute("id", "dot-pattern");
|
|
3389
|
+
pattern.setAttribute("x", "0");
|
|
3390
|
+
pattern.setAttribute("y", "0");
|
|
3391
|
+
pattern.setAttribute("width", "24"); // gap between dots (matching React Flow default)
|
|
3392
|
+
pattern.setAttribute("height", "24");
|
|
3393
|
+
pattern.setAttribute("patternUnits", "userSpaceOnUse");
|
|
3394
|
+
// Create a circle for the dot (centered in the pattern cell)
|
|
3395
|
+
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
3396
|
+
circle.setAttribute("cx", "12"); // Center of 24x24 pattern cell
|
|
3397
|
+
circle.setAttribute("cy", "12");
|
|
3398
|
+
circle.setAttribute("r", "1"); // dot radius = 1 (matching React Flow size={1})
|
|
3399
|
+
circle.setAttribute("fill", backgroundPatternColor);
|
|
3400
|
+
pattern.appendChild(circle);
|
|
3401
|
+
defs.appendChild(pattern);
|
|
3402
|
+
svg.appendChild(defs);
|
|
3403
|
+
// Create pattern overlay rectangle
|
|
3404
|
+
const patternRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
3405
|
+
patternRect.setAttribute("width", String(width));
|
|
3406
|
+
patternRect.setAttribute("height", String(height));
|
|
3407
|
+
patternRect.setAttribute("fill", "url(#dot-pattern)");
|
|
3408
|
+
svg.appendChild(patternRect);
|
|
3409
|
+
}
|
|
3399
3410
|
// Create group with transform
|
|
3400
3411
|
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
3401
3412
|
svg.appendChild(group);
|
|
@@ -3462,13 +3473,16 @@ function renderEdgePath(group, edge) {
|
|
|
3462
3473
|
/**
|
|
3463
3474
|
* Renders all nodes, handles, titles, and edges to SVG group
|
|
3464
3475
|
*/
|
|
3465
|
-
function renderContentToSVG(group, nodes, edges, transformX, transformY) {
|
|
3476
|
+
function renderContentToSVG(group, nodes, edges, transformX, transformY, options = {}) {
|
|
3477
|
+
const { exportHandles = true, exportNodeTitle = true } = options;
|
|
3466
3478
|
group.setAttribute("transform", `translate(${transformX}, ${transformY})`);
|
|
3467
3479
|
// Render nodes
|
|
3468
3480
|
nodes.forEach((node) => {
|
|
3469
3481
|
renderNodeRect(group, node);
|
|
3470
|
-
|
|
3471
|
-
|
|
3482
|
+
if (exportHandles) {
|
|
3483
|
+
node.handles.forEach((handle) => renderHandle(group, handle));
|
|
3484
|
+
}
|
|
3485
|
+
if (exportNodeTitle && node.title) {
|
|
3472
3486
|
renderNodeTitle(group, node.title);
|
|
3473
3487
|
}
|
|
3474
3488
|
});
|
|
@@ -3481,9 +3495,11 @@ function renderContentToSVG(group, nodes, edges, transformX, transformY) {
|
|
|
3481
3495
|
/**
|
|
3482
3496
|
* Captures a React Flow container element as an SVG image and returns data URL
|
|
3483
3497
|
* @param containerElement - The React Flow container DOM element
|
|
3498
|
+
* @param options - Options for thumbnail capture
|
|
3484
3499
|
* @returns Promise resolving to SVG data URL string, or null if capture fails
|
|
3485
3500
|
*/
|
|
3486
|
-
async function captureCanvasThumbnail(containerElement) {
|
|
3501
|
+
async function captureCanvasThumbnail(containerElement, options = {}) {
|
|
3502
|
+
const { ignoreViewport = true, padding = 40, exportHandles = true, exportNodeTitle = true, exportBackgroundPattern = true, nodeBackgroundColor, backgroundPatternColor, } = options;
|
|
3487
3503
|
if (!containerElement) {
|
|
3488
3504
|
console.warn("[flowThumbnail] Container element is null");
|
|
3489
3505
|
return null;
|
|
@@ -3499,12 +3515,18 @@ async function captureCanvasThumbnail(containerElement) {
|
|
|
3499
3515
|
const viewportStyle = window.getComputedStyle(reactFlowViewport);
|
|
3500
3516
|
const viewportTransform = viewportStyle.transform || viewportStyle.getPropertyValue("transform");
|
|
3501
3517
|
const transform = parseViewportTransform(viewportTransform);
|
|
3502
|
-
// Calculate visible bounds
|
|
3518
|
+
// Calculate visible bounds (null if ignoring viewport)
|
|
3503
3519
|
const viewportRect = reactFlowViewport.getBoundingClientRect();
|
|
3504
|
-
const visibleBounds =
|
|
3505
|
-
|
|
3520
|
+
const visibleBounds = ignoreViewport
|
|
3521
|
+
? null
|
|
3522
|
+
: calculateVisibleBounds(viewportRect, transform);
|
|
3523
|
+
// Extract edges and nodes (pass null bounds if ignoring viewport to get all)
|
|
3506
3524
|
const { edges, bounds: edgeBounds } = extractEdgePaths(reactFlowViewport, visibleBounds);
|
|
3507
|
-
const { nodes, bounds: nodeBounds } = extractNodes(reactFlowViewport, visibleBounds
|
|
3525
|
+
const { nodes, bounds: nodeBounds } = extractNodes(reactFlowViewport, visibleBounds, {
|
|
3526
|
+
exportHandles,
|
|
3527
|
+
exportNodeTitle,
|
|
3528
|
+
nodeBackgroundColor,
|
|
3529
|
+
});
|
|
3508
3530
|
// Calculate overall bounding box
|
|
3509
3531
|
// Handle case where one or both bounds might be Infinity (no content)
|
|
3510
3532
|
let minX = Infinity;
|
|
@@ -3523,24 +3545,49 @@ async function captureCanvasThumbnail(containerElement) {
|
|
|
3523
3545
|
maxX = Math.max(maxX, nodeBounds.maxX);
|
|
3524
3546
|
maxY = Math.max(maxY, nodeBounds.maxY);
|
|
3525
3547
|
}
|
|
3526
|
-
// If no
|
|
3548
|
+
// If no content found, return null
|
|
3527
3549
|
if (minX === Infinity || (nodes.length === 0 && edges.length === 0)) {
|
|
3550
|
+
if (ignoreViewport) {
|
|
3551
|
+
console.warn("[flowThumbnail] No content found in graph");
|
|
3552
|
+
return null;
|
|
3553
|
+
}
|
|
3554
|
+
// Fallback to visible viewport bounds if not ignoring viewport
|
|
3528
3555
|
minX = visibleBounds.minX;
|
|
3529
3556
|
minY = visibleBounds.minY;
|
|
3530
3557
|
maxX = visibleBounds.maxX;
|
|
3531
3558
|
maxY = visibleBounds.maxY;
|
|
3532
3559
|
}
|
|
3533
|
-
//
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3560
|
+
// Calculate content bounds with padding if ignoring viewport
|
|
3561
|
+
let contentMinX;
|
|
3562
|
+
let contentMinY;
|
|
3563
|
+
let contentMaxX;
|
|
3564
|
+
let contentMaxY;
|
|
3565
|
+
if (ignoreViewport) {
|
|
3566
|
+
// Add padding when exporting whole graph
|
|
3567
|
+
contentMinX = minX - padding;
|
|
3568
|
+
contentMinY = minY - padding;
|
|
3569
|
+
contentMaxX = maxX + padding;
|
|
3570
|
+
contentMaxY = maxY + padding;
|
|
3571
|
+
}
|
|
3572
|
+
else {
|
|
3573
|
+
// Use the visible viewport bounds exactly (what the user sees)
|
|
3574
|
+
contentMinX = visibleBounds.minX;
|
|
3575
|
+
contentMinY = visibleBounds.minY;
|
|
3576
|
+
contentMaxX = visibleBounds.maxX;
|
|
3577
|
+
contentMaxY = visibleBounds.maxY;
|
|
3578
|
+
}
|
|
3538
3579
|
const contentWidth = contentMaxX - contentMinX;
|
|
3539
3580
|
const contentHeight = contentMaxY - contentMinY;
|
|
3540
|
-
// Create SVG
|
|
3541
|
-
const { svg, group } = createSVGElement(contentWidth, contentHeight
|
|
3542
|
-
|
|
3543
|
-
|
|
3581
|
+
// Create SVG with options
|
|
3582
|
+
const { svg, group } = createSVGElement(contentWidth, contentHeight, {
|
|
3583
|
+
exportBackgroundPattern,
|
|
3584
|
+
backgroundPatternColor,
|
|
3585
|
+
});
|
|
3586
|
+
// Render content with options
|
|
3587
|
+
renderContentToSVG(group, nodes, edges, -contentMinX, -contentMinY, {
|
|
3588
|
+
exportHandles,
|
|
3589
|
+
exportNodeTitle,
|
|
3590
|
+
});
|
|
3544
3591
|
// Serialize SVG to string
|
|
3545
3592
|
const serializer = new XMLSerializer();
|
|
3546
3593
|
const svgString = serializer.serializeToString(svg);
|
|
@@ -3556,10 +3603,11 @@ async function captureCanvasThumbnail(containerElement) {
|
|
|
3556
3603
|
/**
|
|
3557
3604
|
* Captures a React Flow container element as an SVG image and downloads it
|
|
3558
3605
|
* @param containerElement - The React Flow container DOM element
|
|
3606
|
+
* @param options - Options for thumbnail capture
|
|
3559
3607
|
* @returns Promise resolving to true if successful, false otherwise
|
|
3560
3608
|
*/
|
|
3561
|
-
async function downloadCanvasThumbnail(containerElement) {
|
|
3562
|
-
const svgDataUrl = await captureCanvasThumbnail(containerElement);
|
|
3609
|
+
async function downloadCanvasThumbnail(containerElement, options = {}) {
|
|
3610
|
+
const svgDataUrl = await captureCanvasThumbnail(containerElement, options);
|
|
3563
3611
|
if (!svgDataUrl) {
|
|
3564
3612
|
return false;
|
|
3565
3613
|
}
|
|
@@ -4518,24 +4566,24 @@ function IssueBadge({ level, title, size = 12, className, }) {
|
|
|
4518
4566
|
return (jsx("button", { type: "button", className: `inline-flex items-center justify-center shrink-0 ${colorClass} ${className ?? ""}`, title: title, style: { width: size, height: size }, children: level === "error" ? (jsx(XCircleIcon, { size: size, weight: "fill" })) : (jsx(WarningCircleIcon, { size: size, weight: "fill" })) }));
|
|
4519
4567
|
}
|
|
4520
4568
|
|
|
4521
|
-
function DefaultNodeHeader({ id, typeId,
|
|
4569
|
+
function DefaultNodeHeader({ id, typeId, validation, right, showId, onInvalidate, }) {
|
|
4522
4570
|
const ctx = useWorkbenchContext();
|
|
4523
4571
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
4524
4572
|
const [editValue, setEditValue] = React.useState("");
|
|
4525
4573
|
const inputRef = React.useRef(null);
|
|
4526
4574
|
// Use getNodeDisplayName if typeId is provided, otherwise use title prop
|
|
4527
|
-
const displayName = typeId ? ctx.getNodeDisplayName(id) :
|
|
4528
|
-
const effectiveTypeId = typeId ??
|
|
4575
|
+
const displayName = typeId ? ctx.getNodeDisplayName(id) : id;
|
|
4576
|
+
const effectiveTypeId = typeId ?? id;
|
|
4529
4577
|
// Get the default display name (without custom name) for comparison
|
|
4530
4578
|
const getDefaultDisplayName = React.useCallback(() => {
|
|
4531
4579
|
if (!typeId)
|
|
4532
|
-
return
|
|
4580
|
+
return id;
|
|
4533
4581
|
const node = ctx.wb.def.nodes.find((n) => n.nodeId === id);
|
|
4534
4582
|
if (!node)
|
|
4535
4583
|
return id;
|
|
4536
4584
|
const desc = ctx.registry.nodes.get(node.typeId);
|
|
4537
4585
|
return desc?.displayName || node.typeId;
|
|
4538
|
-
}, [ctx, id, typeId
|
|
4586
|
+
}, [ctx, id, typeId]);
|
|
4539
4587
|
const handleInvalidate = React.useCallback(() => {
|
|
4540
4588
|
try {
|
|
4541
4589
|
if (onInvalidate)
|