dishui 0.0.47 → 0.0.49
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/dist/components/pro/CodeEditor/editors/MarkdownEditor.js +1 -2
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/themes.js +0 -25
- package/dist/components/pro/CodeEditor/themes.js.map +1 -1
- package/dist/components/pro/CodeEditor/types.d.ts +1 -1
- package/dist/components/pro/Flow/Flow.js +500 -483
- package/dist/components/pro/Flow/Flow.js.map +1 -1
- package/dist/components/pro/Flow/FlowCanvas.js +526 -524
- package/dist/components/pro/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/pro/Flow/canvas/GridRenderer.d.ts +3 -0
- package/dist/components/pro/Flow/canvas/GridRenderer.js +28 -23
- package/dist/components/pro/Flow/canvas/GridRenderer.js.map +1 -1
- package/dist/components/pro/Flow/canvas/MiniMapRenderer.js +18 -17
- package/dist/components/pro/Flow/canvas/MiniMapRenderer.js.map +1 -1
- package/dist/components/pro/Flow/canvas/PixiApp.d.ts +2 -0
- package/dist/components/pro/Flow/canvas/PixiApp.js +14 -11
- package/dist/components/pro/Flow/canvas/PixiApp.js.map +1 -1
- package/dist/components/pro/Flow/constants.js +7 -7
- package/dist/components/pro/Flow/constants.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowAIContext.d.ts +15 -0
- package/dist/components/pro/Flow/context/FlowAIContext.js +32 -0
- package/dist/components/pro/Flow/context/FlowAIContext.js.map +1 -0
- package/dist/components/pro/Flow/context/FlowUIContext.d.ts +4 -1
- package/dist/components/pro/Flow/context/FlowUIContext.js +58 -55
- package/dist/components/pro/Flow/context/FlowUIContext.js.map +1 -1
- package/dist/components/pro/Flow/edges/EdgeRenderer.js +22 -21
- package/dist/components/pro/Flow/edges/EdgeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useFlowImportExport.js +32 -32
- package/dist/components/pro/Flow/hooks/useFlowImportExport.js.map +1 -1
- package/dist/components/pro/Flow/hooks/usePixiApp.d.ts +6 -1
- package/dist/components/pro/Flow/hooks/usePixiApp.js +49 -30
- package/dist/components/pro/Flow/hooks/usePixiApp.js.map +1 -1
- package/dist/components/pro/Flow/index.d.ts +2 -0
- package/dist/components/pro/Flow/lines/FreehandTool.js +21 -20
- package/dist/components/pro/Flow/lines/FreehandTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/GeometricLineTool.js +22 -18
- package/dist/components/pro/Flow/lines/GeometricLineTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/LineRenderer.js +19 -18
- package/dist/components/pro/Flow/lines/LineRenderer.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.js +143 -124
- package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js +22 -17
- package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeRenderer.js +77 -76
- package/dist/components/pro/Flow/nodes/NodeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/nodes/aiClient.d.ts +22 -0
- package/dist/components/pro/Flow/nodes/aiClient.js +107 -54
- package/dist/components/pro/Flow/nodes/aiClient.js.map +1 -1
- package/dist/components/pro/Flow/nodes/svgIcons.js +6 -6
- package/dist/components/pro/Flow/nodes/svgIcons.js.map +1 -1
- package/dist/components/pro/Flow/panels/AIPanel.d.ts +2 -0
- package/dist/components/pro/Flow/panels/AIPanel.js +498 -0
- package/dist/components/pro/Flow/panels/AIPanel.js.map +1 -0
- package/dist/components/pro/Flow/panels/FlowPanelGroup.js +51 -39
- package/dist/components/pro/Flow/panels/FlowPanelGroup.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowToolbar.js +91 -85
- package/dist/components/pro/Flow/panels/FlowToolbar.js.map +1 -1
- package/dist/components/pro/Flow/panels/InspectorPanel.js +372 -332
- package/dist/components/pro/Flow/panels/InspectorPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/OutlinePanel.js +28 -27
- package/dist/components/pro/Flow/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/ShapePalette.js +107 -104
- package/dist/components/pro/Flow/panels/ShapePalette.js.map +1 -1
- package/dist/components/pro/Flow/panels/toolbarIcons.d.ts +1 -0
- package/dist/components/pro/Flow/panels/toolbarIcons.js +25 -20
- package/dist/components/pro/Flow/panels/toolbarIcons.js.map +1 -1
- package/dist/components/pro/Flow/types.d.ts +13 -0
- package/dist/components/pro/Flow/types.js.map +1 -1
- package/dist/components/pro/Flow/utils/themeColor.d.ts +87 -0
- package/dist/components/pro/Flow/utils/themeColor.js +212 -0
- package/dist/components/pro/Flow/utils/themeColor.js.map +1 -0
- package/dist/contexts/AppContext.d.ts +1 -1
- package/dist/contexts/AppContext.js +28 -29
- package/dist/contexts/AppContext.js.map +1 -1
- package/dist/dishui.css +1 -1
- package/dist/favicon.svg +10 -0
- package/dist/flow.js +56 -51
- package/dist/styles/themeStyles.js +6 -11
- package/dist/styles/themeStyles.js.map +1 -1
- package/dist/utils/themes.js +3 -23
- package/dist/utils/themes.js.map +1 -1
- package/package.json +17 -18
|
@@ -1,92 +1,95 @@
|
|
|
1
|
-
import { createContext as
|
|
2
|
-
import { jsx as
|
|
3
|
-
var G =
|
|
4
|
-
function
|
|
5
|
-
const n =
|
|
1
|
+
import { createContext as ae, useCallback as o, useContext as le, useMemo as we, useRef as i, useState as e } from "react";
|
|
2
|
+
import { jsx as de } from "react/jsx-runtime";
|
|
3
|
+
var G = ae(null);
|
|
4
|
+
function fe() {
|
|
5
|
+
const n = le(G);
|
|
6
6
|
if (!n) throw new Error("useFlowUI must be used within FlowUIProvider");
|
|
7
7
|
return n;
|
|
8
8
|
}
|
|
9
|
-
function
|
|
10
|
-
const [a,
|
|
9
|
+
function Se({ showMiniMap: n = !0, showGrid: b = !0, gridSize: y = 20, showAI: E = !0, children: V }) {
|
|
10
|
+
const [a, J] = e("select"), [l, j] = e("smoothstep"), [w, d] = e({
|
|
11
11
|
x: 0,
|
|
12
12
|
y: 0,
|
|
13
13
|
zoom: 1
|
|
14
|
-
}), h =
|
|
15
|
-
h.current += 1,
|
|
16
|
-
}, []),
|
|
17
|
-
|
|
18
|
-
}, []), [g,
|
|
14
|
+
}), h = i(0), k = o((t) => {
|
|
15
|
+
h.current += 1, d(t);
|
|
16
|
+
}, []), q = o((t) => {
|
|
17
|
+
d(t);
|
|
18
|
+
}, []), [g, B] = e(!1), [p, H] = e(null), r = i(0), [c, f] = e(!1), S = o(() => {
|
|
19
19
|
r.current += 1, c || f(!0);
|
|
20
|
-
}, [c]),
|
|
20
|
+
}, [c]), I = o(() => {
|
|
21
21
|
r.current = Math.max(0, r.current - 1), r.current === 0 && f(!1);
|
|
22
|
-
}, []), [
|
|
23
|
-
|
|
24
|
-
const s = new Set(
|
|
22
|
+
}, []), [M, K] = e(!0), [m, L] = e(!0), [x, Q] = e(!0), [P, W] = e(!0), [R, X] = e(E), [F, Y] = e(n), [v, Z] = e(b), [A, _] = e(!0), [C, $] = e(y), [D, ee] = e(null), [N, te] = e(null), [O, oe] = e(null), [T, se] = e(/* @__PURE__ */ new Set()), U = o((t) => {
|
|
23
|
+
se((u) => {
|
|
24
|
+
const s = new Set(u);
|
|
25
25
|
return s.has(t) ? s.delete(t) : s.add(t), s;
|
|
26
26
|
});
|
|
27
|
-
}, []), z =
|
|
28
|
-
}),
|
|
29
|
-
z.current(t,
|
|
30
|
-
}, []),
|
|
27
|
+
}, []), z = i(() => {
|
|
28
|
+
}), ne = o((t, u, s, ue, ie) => {
|
|
29
|
+
z.current(t, u, s, ue, ie);
|
|
30
|
+
}, []), re = o((t) => {
|
|
31
31
|
z.current = t;
|
|
32
|
-
}, []),
|
|
32
|
+
}, []), ce = we(() => ({
|
|
33
33
|
toolMode: a,
|
|
34
|
-
setToolMode:
|
|
34
|
+
setToolMode: J,
|
|
35
35
|
edgeType: l,
|
|
36
|
-
setEdgeType:
|
|
37
|
-
viewport:
|
|
38
|
-
setViewport:
|
|
39
|
-
setViewportInternal:
|
|
36
|
+
setEdgeType: j,
|
|
37
|
+
viewport: w,
|
|
38
|
+
setViewport: k,
|
|
39
|
+
setViewportInternal: q,
|
|
40
40
|
viewportReqRef: h,
|
|
41
|
-
focusOnRect:
|
|
42
|
-
setFocusOnRect:
|
|
41
|
+
focusOnRect: ne,
|
|
42
|
+
setFocusOnRect: re,
|
|
43
43
|
autoFocus: g,
|
|
44
|
-
setAutoFocus:
|
|
44
|
+
setAutoFocus: B,
|
|
45
45
|
editingNodeId: p,
|
|
46
|
-
setEditingNodeId:
|
|
46
|
+
setEditingNodeId: H,
|
|
47
47
|
isNodeDragging: c,
|
|
48
48
|
beginNodeDrag: S,
|
|
49
|
-
endNodeDrag:
|
|
50
|
-
showPalette:
|
|
51
|
-
setShowPalette:
|
|
52
|
-
showInspector:
|
|
53
|
-
setShowInspector:
|
|
54
|
-
showOutline:
|
|
55
|
-
setShowOutline:
|
|
49
|
+
endNodeDrag: I,
|
|
50
|
+
showPalette: M,
|
|
51
|
+
setShowPalette: K,
|
|
52
|
+
showInspector: m,
|
|
53
|
+
setShowInspector: L,
|
|
54
|
+
showOutline: x,
|
|
55
|
+
setShowOutline: Q,
|
|
56
56
|
showJson: P,
|
|
57
|
-
setShowJson:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
setShowJson: W,
|
|
58
|
+
showAI: R,
|
|
59
|
+
setShowAI: X,
|
|
60
|
+
showMiniMap: F,
|
|
61
|
+
setShowMiniMap: Y,
|
|
62
|
+
showGrid: v,
|
|
63
|
+
setShowGrid: Z,
|
|
64
|
+
showAnchors: A,
|
|
65
|
+
setShowAnchors: _,
|
|
64
66
|
gridSize: C,
|
|
65
|
-
setGridSize:
|
|
67
|
+
setGridSize: $,
|
|
66
68
|
pendingPlacement: D,
|
|
67
|
-
setPendingPlacement:
|
|
69
|
+
setPendingPlacement: ee,
|
|
68
70
|
contextMenu: N,
|
|
69
|
-
setContextMenu:
|
|
71
|
+
setContextMenu: te,
|
|
70
72
|
presentation: O,
|
|
71
|
-
setPresentation:
|
|
73
|
+
setPresentation: oe,
|
|
72
74
|
detachedTabs: T,
|
|
73
75
|
toggleDetach: U
|
|
74
76
|
}), [
|
|
75
77
|
a,
|
|
76
78
|
l,
|
|
77
|
-
|
|
79
|
+
w,
|
|
78
80
|
g,
|
|
79
81
|
p,
|
|
80
82
|
c,
|
|
81
83
|
S,
|
|
84
|
+
I,
|
|
82
85
|
M,
|
|
83
86
|
m,
|
|
84
87
|
x,
|
|
85
|
-
I,
|
|
86
88
|
P,
|
|
87
89
|
R,
|
|
88
90
|
F,
|
|
89
91
|
v,
|
|
92
|
+
A,
|
|
90
93
|
C,
|
|
91
94
|
D,
|
|
92
95
|
N,
|
|
@@ -94,14 +97,14 @@ function ge({ showMiniMap: n = !0, showGrid: b = !0, gridSize: y = 20, children:
|
|
|
94
97
|
T,
|
|
95
98
|
U
|
|
96
99
|
]);
|
|
97
|
-
return /* @__PURE__ */
|
|
98
|
-
value:
|
|
99
|
-
children:
|
|
100
|
+
return /* @__PURE__ */ de(G.Provider, {
|
|
101
|
+
value: ce,
|
|
102
|
+
children: V
|
|
100
103
|
});
|
|
101
104
|
}
|
|
102
105
|
export {
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
Se as FlowUIProvider,
|
|
107
|
+
fe as useFlowUI
|
|
105
108
|
};
|
|
106
109
|
|
|
107
110
|
//# sourceMappingURL=FlowUIContext.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlowUIContext.js","names":[],"sources":["../../../../../src/components/pro/Flow/context/FlowUIContext.tsx"],"sourcesContent":["import React, { createContext, useContext, useMemo, useState, useRef, useCallback } from 'react';\nimport type { FlowToolMode, FlowEdgeTypeName, FlowShapeType, FlowNodeData, ViewportState } from '../types';\n\nexport interface ContextMenuState {\n x: number;\n y: number;\n nodeId: string | null;\n}\n\nexport interface FocusOnRectOptions {\n /** Screen-space padding around the focused rectangle, in pixels. */\n padding?: number;\n /** Maximum zoom allowed for this focus operation. */\n maxZoom?: number;\n /** Viewport transition duration in milliseconds. */\n duration?: number;\n /** Keep the current zoom and only animate the focused rectangle to center. */\n preserveZoom?: boolean;\n}\n\nexport interface FlowUIContextValue {\n toolMode: FlowToolMode;\n setToolMode: (mode: FlowToolMode) => void;\n edgeType: FlowEdgeTypeName;\n setEdgeType: (type: FlowEdgeTypeName) => void;\n viewport: ViewportState;\n setViewport: (v: ViewportState) => void;\n /** Internal VM→React bridge setter (does not request a VM apply). */\n setViewportInternal: (v: ViewportState) => void;\n /** Monotonic counter of external viewport commands. */\n viewportReqRef: React.MutableRefObject<number>;\n focusOnRect: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void;\n setFocusOnRect: (fn: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void) => void;\n autoFocus: boolean;\n setAutoFocus: (v: boolean) => void;\n editingNodeId: string | null;\n setEditingNodeId: (id: string | null) => void;\n /** True while any node/line is being dragged. Used to suppress interactive\n * DOM overlays (e.g. HTML iframes) that would otherwise swallow pointerup. */\n isNodeDragging: boolean;\n beginNodeDrag: () => void;\n endNodeDrag: () => void;\n showPalette: boolean;\n setShowPalette: (v: boolean) => void;\n showInspector: boolean;\n setShowInspector: (v: boolean) => void;\n showOutline: boolean;\n setShowOutline: (v: boolean) => void;\n showJson: boolean;\n setShowJson: (v: boolean) => void;\n showMiniMap: boolean;\n setShowMiniMap: (v: boolean) => void;\n showGrid: boolean;\n setShowGrid: (v: boolean) => void;\n /** Global editor-only visibility for animation anchor objects. */\n showAnchors: boolean;\n setShowAnchors: (v: boolean) => void;\n gridSize: number;\n setGridSize: (s: number) => void;\n pendingPlacement: PendingPlacement | null;\n setPendingPlacement: (p: PendingPlacement | null) => void;\n contextMenu: ContextMenuState | null;\n setContextMenu: (m: ContextMenuState | null) => void;\n detachedTabs: Set<string>;\n toggleDetach: (tabId: string) => void;\n /** PPT-style presentation mode. When set, the overlay animates the viewport\n * between animation anchors, navigable via arrow keys. */\n presentation: PresentationState | null;\n setPresentation: (p: PresentationState | null) => void;\n}\n\nexport interface PresentationState {\n /** Index into the anchor sequence (0-based). */\n current: number;\n /** Whether to show the active anchor frame and sequence badge in preview. */\n showAnchor?: boolean;\n}\n\nexport interface PendingPlacement {\n type: FlowShapeType;\n width: number;\n height: number;\n data: FlowNodeData;\n}\n\nconst FlowUIContext = createContext<FlowUIContextValue | null>(null);\n\nexport function useFlowUI(): FlowUIContextValue {\n const ctx = useContext(FlowUIContext);\n if (!ctx) throw new Error('useFlowUI must be used within FlowUIProvider');\n return ctx;\n}\n\nexport interface FlowUIProviderProps {\n showMiniMap?: boolean;\n showGrid?: boolean;\n gridSize?: number;\n children: React.ReactNode;\n}\n\nexport function FlowUIProvider({ showMiniMap: initMiniMap = true, showGrid: initGrid = true, gridSize: initGridSize = 20, children }: FlowUIProviderProps) {\n const [toolMode, setToolMode] = useState<FlowToolMode>('select');\n const [edgeType, setEdgeType] = useState<FlowEdgeTypeName>('smoothstep');\n const [viewport, setViewportState] = useState<ViewportState>({ x: 0, y: 0, zoom: 1 });\n // Distinguishes EXTERNAL viewport commands (toolbar, minimap, outline) from\n // internal echoes emitted by the ViewportManager. Only external commands\n // should be pushed back into the VM; echoing VM changes causes jitter.\n const viewportReqRef = useRef(0);\n const setViewport = useCallback((v: ViewportState) => {\n viewportReqRef.current += 1;\n setViewportState(v);\n }, []);\n // Internal setter used by the VM→React bridge; does NOT bump the request\n // counter, so the sync effect will not echo it back to the VM.\n const setViewportInternal = useCallback((v: ViewportState) => {\n setViewportState(v);\n }, []);\n const [autoFocus, setAutoFocus] = useState(false);\n const [editingNodeId, setEditingNodeId] = useState<string | null>(null);\n // Counter so concurrent drags (multi-node) don't flip the flag early.\n const dragCountRef = useRef(0);\n const [isNodeDragging, setIsNodeDragging] = useState(false);\n const beginNodeDrag = useCallback(() => {\n dragCountRef.current += 1;\n if (!isNodeDragging) setIsNodeDragging(true);\n }, [isNodeDragging]);\n const endNodeDrag = useCallback(() => {\n dragCountRef.current = Math.max(0, dragCountRef.current - 1);\n if (dragCountRef.current === 0) setIsNodeDragging(false);\n }, []);\n const [showPalette, setShowPalette] = useState(true);\n const [showInspector, setShowInspector] = useState(true);\n const [showOutline, setShowOutline] = useState(true);\n const [showJson, setShowJson] = useState(true);\n const [showMiniMap, setShowMiniMap] = useState(initMiniMap);\n const [showGrid, setShowGrid] = useState(initGrid);\n const [showAnchors, setShowAnchors] = useState(true);\n const [gridSize, setGridSize] = useState(initGridSize);\n const [pendingPlacement, setPendingPlacement] = useState<PendingPlacement | null>(null);\n const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);\n const [presentation, setPresentation] = useState<PresentationState | null>(null);\n const [detachedTabs, setDetachedTabs] = useState<Set<string>>(new Set());\n const toggleDetach = useCallback((tabId: string) => {\n setDetachedTabs(prev => {\n const next = new Set(prev);\n if (next.has(tabId)) next.delete(tabId); else next.add(tabId);\n return next;\n });\n }, []);\n\n const focusOnRectRef = useRef<(wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void>(() => {});\n const focusOnRect = useCallback((wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => {\n focusOnRectRef.current(wx, wy, ww, wh, options);\n }, []);\n const setFocusOnRect = useCallback((fn: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void) => {\n focusOnRectRef.current = fn;\n }, []);\n\n const value = useMemo<FlowUIContextValue>(() => ({\n toolMode, setToolMode,\n edgeType, setEdgeType,\n viewport, setViewport,\n setViewportInternal,\n viewportReqRef,\n focusOnRect, setFocusOnRect,\n autoFocus, setAutoFocus,\n editingNodeId, setEditingNodeId,\n isNodeDragging, beginNodeDrag, endNodeDrag,\n showPalette, setShowPalette,\n showInspector, setShowInspector,\n showOutline, setShowOutline,\n showJson, setShowJson,\n showMiniMap, setShowMiniMap,\n showGrid, setShowGrid,\n showAnchors, setShowAnchors,\n gridSize, setGridSize,\n pendingPlacement, setPendingPlacement,\n contextMenu, setContextMenu,\n presentation, setPresentation,\n detachedTabs, toggleDetach,\n }), [toolMode, edgeType, viewport, autoFocus, editingNodeId, isNodeDragging, beginNodeDrag, endNodeDrag, showPalette, showInspector, showOutline, showJson, showMiniMap, showGrid, showAnchors, gridSize, pendingPlacement, contextMenu, presentation, detachedTabs, toggleDetach]);\n\n return <FlowUIContext.Provider value={value}>{children}</FlowUIContext.Provider>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FlowUIContext.js","names":[],"sources":["../../../../../src/components/pro/Flow/context/FlowUIContext.tsx"],"sourcesContent":["import React, { createContext, useContext, useMemo, useState, useRef, useCallback } from 'react';\nimport type { FlowToolMode, FlowEdgeTypeName, FlowShapeType, FlowNodeData, ViewportState } from '../types';\n\nexport interface ContextMenuState {\n x: number;\n y: number;\n nodeId: string | null;\n}\n\nexport interface FocusOnRectOptions {\n /** Screen-space padding around the focused rectangle, in pixels. */\n padding?: number;\n /** Maximum zoom allowed for this focus operation. */\n maxZoom?: number;\n /** Viewport transition duration in milliseconds. */\n duration?: number;\n /** Keep the current zoom and only animate the focused rectangle to center. */\n preserveZoom?: boolean;\n}\n\nexport interface FlowUIContextValue {\n toolMode: FlowToolMode;\n setToolMode: (mode: FlowToolMode) => void;\n edgeType: FlowEdgeTypeName;\n setEdgeType: (type: FlowEdgeTypeName) => void;\n viewport: ViewportState;\n setViewport: (v: ViewportState) => void;\n /** Internal VM→React bridge setter (does not request a VM apply). */\n setViewportInternal: (v: ViewportState) => void;\n /** Monotonic counter of external viewport commands. */\n viewportReqRef: React.MutableRefObject<number>;\n focusOnRect: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void;\n setFocusOnRect: (fn: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void) => void;\n autoFocus: boolean;\n setAutoFocus: (v: boolean) => void;\n editingNodeId: string | null;\n setEditingNodeId: (id: string | null) => void;\n /** True while any node/line is being dragged. Used to suppress interactive\n * DOM overlays (e.g. HTML iframes) that would otherwise swallow pointerup. */\n isNodeDragging: boolean;\n beginNodeDrag: () => void;\n endNodeDrag: () => void;\n showPalette: boolean;\n setShowPalette: (v: boolean) => void;\n showInspector: boolean;\n setShowInspector: (v: boolean) => void;\n showOutline: boolean;\n setShowOutline: (v: boolean) => void;\n showJson: boolean;\n setShowJson: (v: boolean) => void;\n showAI: boolean;\n setShowAI: (v: boolean) => void;\n showMiniMap: boolean;\n setShowMiniMap: (v: boolean) => void;\n showGrid: boolean;\n setShowGrid: (v: boolean) => void;\n /** Global editor-only visibility for animation anchor objects. */\n showAnchors: boolean;\n setShowAnchors: (v: boolean) => void;\n gridSize: number;\n setGridSize: (s: number) => void;\n pendingPlacement: PendingPlacement | null;\n setPendingPlacement: (p: PendingPlacement | null) => void;\n contextMenu: ContextMenuState | null;\n setContextMenu: (m: ContextMenuState | null) => void;\n detachedTabs: Set<string>;\n toggleDetach: (tabId: string) => void;\n /** PPT-style presentation mode. When set, the overlay animates the viewport\n * between animation anchors, navigable via arrow keys. */\n presentation: PresentationState | null;\n setPresentation: (p: PresentationState | null) => void;\n}\n\nexport interface PresentationState {\n /** Index into the anchor sequence (0-based). */\n current: number;\n /** Whether to show the active anchor frame and sequence badge in preview. */\n showAnchor?: boolean;\n}\n\nexport interface PendingPlacement {\n type: FlowShapeType;\n width: number;\n height: number;\n data: FlowNodeData;\n}\n\nconst FlowUIContext = createContext<FlowUIContextValue | null>(null);\n\nexport function useFlowUI(): FlowUIContextValue {\n const ctx = useContext(FlowUIContext);\n if (!ctx) throw new Error('useFlowUI must be used within FlowUIProvider');\n return ctx;\n}\n\nexport interface FlowUIProviderProps {\n showMiniMap?: boolean;\n showGrid?: boolean;\n gridSize?: number;\n showAI?: boolean;\n children: React.ReactNode;\n}\n\nexport function FlowUIProvider({ showMiniMap: initMiniMap = true, showGrid: initGrid = true, gridSize: initGridSize = 20, showAI: initShowAI = true, children }: FlowUIProviderProps) {\n const [toolMode, setToolMode] = useState<FlowToolMode>('select');\n const [edgeType, setEdgeType] = useState<FlowEdgeTypeName>('smoothstep');\n const [viewport, setViewportState] = useState<ViewportState>({ x: 0, y: 0, zoom: 1 });\n // Distinguishes EXTERNAL viewport commands (toolbar, minimap, outline) from\n // internal echoes emitted by the ViewportManager. Only external commands\n // should be pushed back into the VM; echoing VM changes causes jitter.\n const viewportReqRef = useRef(0);\n const setViewport = useCallback((v: ViewportState) => {\n viewportReqRef.current += 1;\n setViewportState(v);\n }, []);\n // Internal setter used by the VM→React bridge; does NOT bump the request\n // counter, so the sync effect will not echo it back to the VM.\n const setViewportInternal = useCallback((v: ViewportState) => {\n setViewportState(v);\n }, []);\n const [autoFocus, setAutoFocus] = useState(false);\n const [editingNodeId, setEditingNodeId] = useState<string | null>(null);\n // Counter so concurrent drags (multi-node) don't flip the flag early.\n const dragCountRef = useRef(0);\n const [isNodeDragging, setIsNodeDragging] = useState(false);\n const beginNodeDrag = useCallback(() => {\n dragCountRef.current += 1;\n if (!isNodeDragging) setIsNodeDragging(true);\n }, [isNodeDragging]);\n const endNodeDrag = useCallback(() => {\n dragCountRef.current = Math.max(0, dragCountRef.current - 1);\n if (dragCountRef.current === 0) setIsNodeDragging(false);\n }, []);\n const [showPalette, setShowPalette] = useState(true);\n const [showInspector, setShowInspector] = useState(true);\n const [showOutline, setShowOutline] = useState(true);\n const [showJson, setShowJson] = useState(true);\n const [showAI, setShowAI] = useState(initShowAI);\n const [showMiniMap, setShowMiniMap] = useState(initMiniMap);\n const [showGrid, setShowGrid] = useState(initGrid);\n const [showAnchors, setShowAnchors] = useState(true);\n const [gridSize, setGridSize] = useState(initGridSize);\n const [pendingPlacement, setPendingPlacement] = useState<PendingPlacement | null>(null);\n const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);\n const [presentation, setPresentation] = useState<PresentationState | null>(null);\n const [detachedTabs, setDetachedTabs] = useState<Set<string>>(new Set());\n const toggleDetach = useCallback((tabId: string) => {\n setDetachedTabs(prev => {\n const next = new Set(prev);\n if (next.has(tabId)) next.delete(tabId); else next.add(tabId);\n return next;\n });\n }, []);\n\n const focusOnRectRef = useRef<(wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void>(() => {});\n const focusOnRect = useCallback((wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => {\n focusOnRectRef.current(wx, wy, ww, wh, options);\n }, []);\n const setFocusOnRect = useCallback((fn: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void) => {\n focusOnRectRef.current = fn;\n }, []);\n\n const value = useMemo<FlowUIContextValue>(() => ({\n toolMode, setToolMode,\n edgeType, setEdgeType,\n viewport, setViewport,\n setViewportInternal,\n viewportReqRef,\n focusOnRect, setFocusOnRect,\n autoFocus, setAutoFocus,\n editingNodeId, setEditingNodeId,\n isNodeDragging, beginNodeDrag, endNodeDrag,\n showPalette, setShowPalette,\n showInspector, setShowInspector,\n showOutline, setShowOutline,\n showJson, setShowJson,\n showAI, setShowAI,\n showMiniMap, setShowMiniMap,\n showGrid, setShowGrid,\n showAnchors, setShowAnchors,\n gridSize, setGridSize,\n pendingPlacement, setPendingPlacement,\n contextMenu, setContextMenu,\n presentation, setPresentation,\n detachedTabs, toggleDetach,\n }), [toolMode, edgeType, viewport, autoFocus, editingNodeId, isNodeDragging, beginNodeDrag, endNodeDrag, showPalette, showInspector, showOutline, showJson, showAI, showMiniMap, showGrid, showAnchors, gridSize, pendingPlacement, contextMenu, presentation, detachedTabs, toggleDetach]);\n\n return <FlowUIContext.Provider value={value}>{children}</FlowUIContext.Provider>;\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveThemeColor as E } from "../utils/themeColor.js";
|
|
2
|
+
import { buildEdgePath as T, getPathCenter as B, resolveEdgeEndpoints as M } from "./edgePaths.js";
|
|
2
3
|
import { angleBetween as k, drawMarker as _ } from "./edgeMarkers.js";
|
|
3
4
|
import { drawDashedPath as y } from "../utils/dashedLine.js";
|
|
4
|
-
import { Container as
|
|
5
|
-
function
|
|
5
|
+
import { Container as v, Graphics as x, Text as L, TextStyle as O } from "pixi.js";
|
|
6
|
+
function P(e, l, a = 2) {
|
|
6
7
|
if (!(!e || e === "solid"))
|
|
7
8
|
return e === "dynamic" ? [a * 4, a * 3] : [e === "dotted" ? a : a * 3, l === "dense" ? a : l === "sparse" ? a * 4 : a * 2];
|
|
8
9
|
}
|
|
9
|
-
var
|
|
10
|
+
var R = class {
|
|
10
11
|
world;
|
|
11
12
|
edgeLayer;
|
|
12
13
|
edges = /* @__PURE__ */ new Map();
|
|
@@ -15,13 +16,13 @@ var F = class {
|
|
|
15
16
|
this._dynamicOffset = (this._dynamicOffset + e * 0.06) % 1e3;
|
|
16
17
|
}
|
|
17
18
|
constructor(e) {
|
|
18
|
-
this.world = e, this.edgeLayer = new
|
|
19
|
+
this.world = e, this.edgeLayer = new v({ label: "edges" }), this.world.addChildAt(this.edgeLayer, 0);
|
|
19
20
|
}
|
|
20
21
|
sync(e, l, a, i, c) {
|
|
21
22
|
const g = /* @__PURE__ */ new Set();
|
|
22
23
|
for (const r of e) {
|
|
23
24
|
g.add(r.id);
|
|
24
|
-
const t =
|
|
25
|
+
const t = M(r, l);
|
|
25
26
|
if (!t) continue;
|
|
26
27
|
let h = this.edges.get(r.id);
|
|
27
28
|
h || (h = this._createEdge(r), this.edges.set(r.id, h)), this._updateEdge(h, r, t, a.has(r.id), i === r.id, c?.presentation ?? !1);
|
|
@@ -29,7 +30,7 @@ var F = class {
|
|
|
29
30
|
for (const [r, t] of this.edges) g.has(r) || (this.edgeLayer.removeChild(t.container), t.container.destroy({ children: !0 }), this.edges.delete(r));
|
|
30
31
|
}
|
|
31
32
|
_createEdge(e) {
|
|
32
|
-
const l = new
|
|
33
|
+
const l = new v({ label: `edge-${e.id}` }), a = new x();
|
|
33
34
|
a.label = "edge-hitarea", a.eventMode = "static", a.cursor = "pointer", a.alpha = 0, l.addChild(a);
|
|
34
35
|
const i = new x();
|
|
35
36
|
i.label = "edge-line", l.addChild(i);
|
|
@@ -45,8 +46,8 @@ var F = class {
|
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
_updateEdge(e, l, a, i, c, g) {
|
|
48
|
-
const { line: r, animation: t, hitArea: h } = e, o = l.data,
|
|
49
|
-
if (r.clear(), this._drawPath(r, s,
|
|
49
|
+
const { line: r, animation: t, hitArea: h } = e, o = l.data, m = i ? "#3b82f6" : E(o.strokeColor, "#374151"), f = o.strokeWidth ?? 2, s = T(l.type, a, o.waypoints, o.bezierControls);
|
|
50
|
+
if (r.clear(), this._drawPath(r, s, m, f, o.lineStyle, o.dashDensity), t.clear(), g && o.animated) {
|
|
50
51
|
const n = [Math.max(18, f * 12), Math.max(90, f * 45)];
|
|
51
52
|
y(t, s, n, -this._dynamicOffset * 2.2), t.stroke({
|
|
52
53
|
color: 6333946,
|
|
@@ -66,33 +67,33 @@ var F = class {
|
|
|
66
67
|
});
|
|
67
68
|
}
|
|
68
69
|
if (o.markerStart && o.markerStart !== "none" && s.length >= 2) {
|
|
69
|
-
const n = s[0], d = s[1],
|
|
70
|
-
_(r, o.markerStart, n.x, n.y,
|
|
70
|
+
const n = s[0], d = s[1], b = k(d.type === "C" ? d.cp1x : d.x, d.type === "C" ? d.cp1y : d.y, n.x, n.y);
|
|
71
|
+
_(r, o.markerStart, n.x, n.y, b, m);
|
|
71
72
|
}
|
|
72
73
|
if (o.markerEnd && o.markerEnd !== "none" && s.length >= 2) {
|
|
73
74
|
const n = s[s.length - 1], d = k(n.type === "C" ? n.cp2x : s[s.length - 2].x, n.type === "C" ? n.cp2y : s[s.length - 2].y, n.x, n.y);
|
|
74
|
-
_(r, o.markerEnd, n.x, n.y, d,
|
|
75
|
+
_(r, o.markerEnd, n.x, n.y, d, m);
|
|
75
76
|
}
|
|
76
77
|
if (h.clear(), this._drawPath(h, s, "#000000", Math.max(f + 10, 14), "solid"), h.alpha = 1e-3, o.labelText) {
|
|
77
78
|
const n = B(s);
|
|
78
79
|
if (!e.label) {
|
|
79
80
|
const p = new x();
|
|
80
81
|
p.label = "edge-label-bg", e.container.addChild(p), e.labelBg = p;
|
|
81
|
-
const u = new
|
|
82
|
+
const u = new O({
|
|
82
83
|
fontFamily: o.labelFontFamily ?? "system-ui, -apple-system, sans-serif",
|
|
83
84
|
fontSize: o.labelFontSize ?? 12,
|
|
84
|
-
fill:
|
|
85
|
+
fill: m
|
|
85
86
|
});
|
|
86
|
-
e.label = new
|
|
87
|
+
e.label = new L({
|
|
87
88
|
text: o.labelText,
|
|
88
89
|
style: u
|
|
89
90
|
}), e.label.anchor.set(0.5, 0.5), e.container.addChild(e.label);
|
|
90
91
|
}
|
|
91
92
|
e.label.text = o.labelText;
|
|
92
|
-
const d = n.x,
|
|
93
|
-
if (e.label.position.set(d,
|
|
94
|
-
const
|
|
95
|
-
e.labelBg.clear(), e.labelBg.roundRect(d -
|
|
93
|
+
const d = n.x, b = n.y - 10;
|
|
94
|
+
if (e.label.position.set(d, b), e.label.style.fill = m, e.labelBg) {
|
|
95
|
+
const C = e.label.width + 10, w = e.label.height + 4;
|
|
96
|
+
e.labelBg.clear(), e.labelBg.roundRect(d - C / 2, b - w / 2, C, w, 4).fill({
|
|
96
97
|
color: 16777215,
|
|
97
98
|
alpha: 0.92
|
|
98
99
|
});
|
|
@@ -101,7 +102,7 @@ var F = class {
|
|
|
101
102
|
e.label && (e.label.text = ""), e.labelBg && e.labelBg.clear();
|
|
102
103
|
}
|
|
103
104
|
_drawPath(e, l, a, i, c, g) {
|
|
104
|
-
const r =
|
|
105
|
+
const r = P(c, g, i);
|
|
105
106
|
if (r)
|
|
106
107
|
y(e, l, r, c === "dynamic" ? this._dynamicOffset : 0), e.stroke({
|
|
107
108
|
color: a,
|
|
@@ -135,7 +136,7 @@ var F = class {
|
|
|
135
136
|
}
|
|
136
137
|
};
|
|
137
138
|
export {
|
|
138
|
-
|
|
139
|
+
R as EdgeRenderer
|
|
139
140
|
};
|
|
140
141
|
|
|
141
142
|
//# sourceMappingURL=EdgeRenderer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EdgeRenderer.js","names":[],"sources":["../../../../../src/components/pro/Flow/edges/EdgeRenderer.ts"],"sourcesContent":["import { Container, Graphics, Text, TextStyle } from 'pixi.js';\nimport type { FlowEdge, FlowNode } from '../types';\nimport { buildEdgePath, getPathCenter, resolveEdgeEndpoints, type PathSegment, type EdgeEndpoints } from './edgePaths';\nimport { drawMarker, angleBetween } from './edgeMarkers';\nimport { drawDashedPath, type PathSeg } from '../utils/dashedLine';\n\ninterface EdgeDisplay {\n container: Container;\n line: Graphics;\n animation: Graphics;\n hitArea: Graphics;\n labelBg: Graphics | null;\n label: Text | null;\n edgeId: string;\n}\n\nfunction getDashPattern(style?: string, density?: string, width = 2): number[] | undefined {\n if (!style || style === 'solid') return undefined;\n if (style === 'dynamic') return [width * 4, width * 3];\n const base = style === 'dotted' ? width : width * 3;\n const gap = density === 'dense' ? width : density === 'sparse' ? width * 4 : width * 2;\n return [base, gap];\n}\n\n/**\n * Manages PixiJS display objects for all edges.\n */\nexport class EdgeRenderer {\n private world: Container;\n private edgeLayer: Container;\n private edges = new Map<string, EdgeDisplay>();\n private _dynamicOffset = 0;\n\n /** Call once per frame to advance the dynamic dash animation. */\n advanceAnimation(deltaMs: number): void {\n this._dynamicOffset = (this._dynamicOffset + deltaMs * 0.06) % 1000;\n }\n\n constructor(world: Container) {\n this.world = world;\n this.edgeLayer = new Container({ label: 'edges' });\n // Edges below nodes\n this.world.addChildAt(this.edgeLayer, 0);\n }\n\n sync(\n flowEdges: FlowEdge[],\n nodeMap: Map<string, FlowNode>,\n selectedIds: Set<string>,\n hoveredId: string | null,\n options?: { presentation?: boolean },\n ): void {\n const seen = new Set<string>();\n\n for (const edge of flowEdges) {\n seen.add(edge.id);\n const endpoints = resolveEdgeEndpoints(edge, nodeMap);\n if (!endpoints) continue;\n\n let display = this.edges.get(edge.id);\n if (!display) {\n display = this._createEdge(edge);\n this.edges.set(edge.id, display);\n }\n this._updateEdge(\n display,\n edge,\n endpoints,\n selectedIds.has(edge.id),\n hoveredId === edge.id,\n options?.presentation ?? false,\n );\n }\n\n for (const [id, display] of this.edges) {\n if (!seen.has(id)) {\n this.edgeLayer.removeChild(display.container);\n display.container.destroy({ children: true });\n this.edges.delete(id);\n }\n }\n }\n\n private _createEdge(edge: FlowEdge): EdgeDisplay {\n const container = new Container({ label: `edge-${edge.id}` });\n\n const hitArea = new Graphics();\n hitArea.label = 'edge-hitarea';\n hitArea.eventMode = 'static';\n hitArea.cursor = 'pointer';\n hitArea.alpha = 0;\n container.addChild(hitArea);\n\n const line = new Graphics();\n line.label = 'edge-line';\n container.addChild(line);\n\n const animation = new Graphics();\n animation.label = 'edge-animation';\n container.addChild(animation);\n\n this.edgeLayer.addChild(container);\n return { container, line, animation, hitArea, labelBg: null, label: null, edgeId: edge.id };\n }\n\n private _updateEdge(\n display: EdgeDisplay,\n edge: FlowEdge,\n endpoints: EdgeEndpoints,\n selected: boolean,\n hovered: boolean,\n presentation: boolean,\n ): void {\n const { line, animation, hitArea } = display;\n const data = edge.data;\n const color = selected ? '#3b82f6' : (data.strokeColor ?? '#374151');\n const width = data.strokeWidth ?? 2;\n\n const segments = buildEdgePath(edge.type, endpoints, data.waypoints, data.bezierControls);\n\n // Draw visible line\n line.clear();\n this._drawPath(line, segments, color, width, data.lineStyle, data.dashDensity);\n\n // Presentation animation: a bright gradient-like pulse travels from source\n // to target. A short dashed segment plus soft outer stroke gives the pulse\n // a leading highlight and fading tail without requiring SVG filters.\n animation.clear();\n if (presentation && data.animated) {\n const pattern = [Math.max(18, width * 12), Math.max(90, width * 45)];\n drawDashedPath(animation, segments as PathSeg[], pattern, -this._dynamicOffset * 2.2);\n animation.stroke({ color: 0x60a5fa, width: width + 5, alpha: 0.18, cap: 'round' as const });\n drawDashedPath(animation, segments as PathSeg[], pattern, -this._dynamicOffset * 2.2);\n animation.stroke({ color: 0x38bdf8, width: width + 2, alpha: 0.55, cap: 'round' as const });\n drawDashedPath(animation, segments as PathSeg[], pattern, -this._dynamicOffset * 2.2);\n animation.stroke({ color: 0xffffff, width: Math.max(1, width * 0.7), alpha: 0.9, cap: 'round' as const });\n }\n\n // Draw markers — use curve tangent for accurate arrow direction\n if (data.markerStart && data.markerStart !== 'none' && segments.length >= 2) {\n const tip = segments[0];\n const next = segments[1];\n // For curves the tangent at t=0 is tip→cp1; for lines it's tip→next\n const fromX = next.type === 'C' ? next.cp1x! : next.x;\n const fromY = next.type === 'C' ? next.cp1y! : next.y;\n const angle = angleBetween(fromX, fromY, tip.x, tip.y);\n drawMarker(line, data.markerStart, tip.x, tip.y, angle, color);\n }\n if (data.markerEnd && data.markerEnd !== 'none' && segments.length >= 2) {\n const tip = segments[segments.length - 1];\n // For curves the tangent at t=1 is cp2→endpoint\n const fromX = tip.type === 'C' ? tip.cp2x! : segments[segments.length - 2].x;\n const fromY = tip.type === 'C' ? tip.cp2y! : segments[segments.length - 2].y;\n const angle = angleBetween(fromX, fromY, tip.x, tip.y);\n drawMarker(line, data.markerEnd, tip.x, tip.y, angle, color);\n }\n\n // Wider invisible hit area\n hitArea.clear();\n this._drawPath(hitArea, segments, '#000000', Math.max(width + 10, 14), 'solid');\n hitArea.alpha = 0.001;\n\n // Edge label\n if (data.labelText) {\n const center = getPathCenter(segments);\n if (!display.label) {\n // Background chip drawn first so it sits behind the text.\n const labelBg = new Graphics();\n labelBg.label = 'edge-label-bg';\n display.container.addChild(labelBg);\n display.labelBg = labelBg;\n\n const style = new TextStyle({\n fontFamily: data.labelFontFamily ?? 'system-ui, -apple-system, sans-serif',\n fontSize: data.labelFontSize ?? 12,\n fill: color,\n });\n display.label = new Text({ text: data.labelText, style });\n display.label.anchor.set(0.5, 0.5);\n display.container.addChild(display.label);\n }\n display.label.text = data.labelText;\n const lx = center.x;\n const ly = center.y - 10;\n display.label.position.set(lx, ly);\n (display.label.style as TextStyle).fill = color;\n\n // Draw a rounded background chip behind the label so the line / waypoints\n // don't show through the text.\n if (display.labelBg) {\n const padX = 5;\n const padY = 2;\n const tw = display.label.width + padX * 2;\n const th = display.label.height + padY * 2;\n display.labelBg.clear();\n display.labelBg\n .roundRect(lx - tw / 2, ly - th / 2, tw, th, 4)\n .fill({ color: 0xffffff, alpha: 0.92 });\n }\n } else {\n if (display.label) display.label.text = '';\n if (display.labelBg) display.labelBg.clear();\n }\n }\n\n private _drawPath(\n g: Graphics,\n segs: PathSegment[],\n color: string,\n width: number,\n lineStyle?: string,\n dashDensity?: string,\n ): void {\n const dash = getDashPattern(lineStyle, dashDensity, width);\n if (dash) {\n const offset = lineStyle === 'dynamic' ? this._dynamicOffset : 0;\n drawDashedPath(g, segs as PathSeg[], dash, offset);\n g.stroke({ color, width, cap: 'round' as const });\n } else {\n for (const seg of segs) {\n switch (seg.type) {\n case 'M': g.moveTo(seg.x, seg.y); break;\n case 'L': g.lineTo(seg.x, seg.y); break;\n case 'C': g.bezierCurveTo(seg.cp1x!, seg.cp1y!, seg.cp2x!, seg.cp2y!, seg.x, seg.y); break;\n case 'Q': g.quadraticCurveTo(seg.cp1x!, seg.cp1y!, seg.x, seg.y); break;\n }\n }\n g.stroke({ color, width });\n }\n }\n\n destroy(): void {\n for (const [, display] of this.edges) {\n display.container.destroy({ children: true });\n }\n this.edges.clear();\n this.edgeLayer.destroy({ children: true });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EdgeRenderer.js","names":[],"sources":["../../../../../src/components/pro/Flow/edges/EdgeRenderer.ts"],"sourcesContent":["import { Container, Graphics, Text, TextStyle } from 'pixi.js';\nimport type { FlowEdge, FlowNode } from '../types';\nimport { buildEdgePath, getPathCenter, resolveEdgeEndpoints, type PathSegment, type EdgeEndpoints } from './edgePaths';\nimport { drawMarker, angleBetween } from './edgeMarkers';\nimport { drawDashedPath, type PathSeg } from '../utils/dashedLine';\nimport { resolveThemeColor } from '../utils/themeColor';\n\ninterface EdgeDisplay {\n container: Container;\n line: Graphics;\n animation: Graphics;\n hitArea: Graphics;\n labelBg: Graphics | null;\n label: Text | null;\n edgeId: string;\n}\n\nfunction getDashPattern(style?: string, density?: string, width = 2): number[] | undefined {\n if (!style || style === 'solid') return undefined;\n if (style === 'dynamic') return [width * 4, width * 3];\n const base = style === 'dotted' ? width : width * 3;\n const gap = density === 'dense' ? width : density === 'sparse' ? width * 4 : width * 2;\n return [base, gap];\n}\n\n/**\n * Manages PixiJS display objects for all edges.\n */\nexport class EdgeRenderer {\n private world: Container;\n private edgeLayer: Container;\n private edges = new Map<string, EdgeDisplay>();\n private _dynamicOffset = 0;\n\n /** Call once per frame to advance the dynamic dash animation. */\n advanceAnimation(deltaMs: number): void {\n this._dynamicOffset = (this._dynamicOffset + deltaMs * 0.06) % 1000;\n }\n\n constructor(world: Container) {\n this.world = world;\n this.edgeLayer = new Container({ label: 'edges' });\n // Edges below nodes\n this.world.addChildAt(this.edgeLayer, 0);\n }\n\n sync(\n flowEdges: FlowEdge[],\n nodeMap: Map<string, FlowNode>,\n selectedIds: Set<string>,\n hoveredId: string | null,\n options?: { presentation?: boolean },\n ): void {\n const seen = new Set<string>();\n\n for (const edge of flowEdges) {\n seen.add(edge.id);\n const endpoints = resolveEdgeEndpoints(edge, nodeMap);\n if (!endpoints) continue;\n\n let display = this.edges.get(edge.id);\n if (!display) {\n display = this._createEdge(edge);\n this.edges.set(edge.id, display);\n }\n this._updateEdge(\n display,\n edge,\n endpoints,\n selectedIds.has(edge.id),\n hoveredId === edge.id,\n options?.presentation ?? false,\n );\n }\n\n for (const [id, display] of this.edges) {\n if (!seen.has(id)) {\n this.edgeLayer.removeChild(display.container);\n display.container.destroy({ children: true });\n this.edges.delete(id);\n }\n }\n }\n\n private _createEdge(edge: FlowEdge): EdgeDisplay {\n const container = new Container({ label: `edge-${edge.id}` });\n\n const hitArea = new Graphics();\n hitArea.label = 'edge-hitarea';\n hitArea.eventMode = 'static';\n hitArea.cursor = 'pointer';\n hitArea.alpha = 0;\n container.addChild(hitArea);\n\n const line = new Graphics();\n line.label = 'edge-line';\n container.addChild(line);\n\n const animation = new Graphics();\n animation.label = 'edge-animation';\n container.addChild(animation);\n\n this.edgeLayer.addChild(container);\n return { container, line, animation, hitArea, labelBg: null, label: null, edgeId: edge.id };\n }\n\n private _updateEdge(\n display: EdgeDisplay,\n edge: FlowEdge,\n endpoints: EdgeEndpoints,\n selected: boolean,\n hovered: boolean,\n presentation: boolean,\n ): void {\n const { line, animation, hitArea } = display;\n const data = edge.data;\n const color = selected ? '#3b82f6' : resolveThemeColor(data.strokeColor, '#374151');\n const width = data.strokeWidth ?? 2;\n\n const segments = buildEdgePath(edge.type, endpoints, data.waypoints, data.bezierControls);\n\n // Draw visible line\n line.clear();\n this._drawPath(line, segments, color, width, data.lineStyle, data.dashDensity);\n\n // Presentation animation: a bright gradient-like pulse travels from source\n // to target. A short dashed segment plus soft outer stroke gives the pulse\n // a leading highlight and fading tail without requiring SVG filters.\n animation.clear();\n if (presentation && data.animated) {\n const pattern = [Math.max(18, width * 12), Math.max(90, width * 45)];\n drawDashedPath(animation, segments as PathSeg[], pattern, -this._dynamicOffset * 2.2);\n animation.stroke({ color: 0x60a5fa, width: width + 5, alpha: 0.18, cap: 'round' as const });\n drawDashedPath(animation, segments as PathSeg[], pattern, -this._dynamicOffset * 2.2);\n animation.stroke({ color: 0x38bdf8, width: width + 2, alpha: 0.55, cap: 'round' as const });\n drawDashedPath(animation, segments as PathSeg[], pattern, -this._dynamicOffset * 2.2);\n animation.stroke({ color: 0xffffff, width: Math.max(1, width * 0.7), alpha: 0.9, cap: 'round' as const });\n }\n\n // Draw markers — use curve tangent for accurate arrow direction\n if (data.markerStart && data.markerStart !== 'none' && segments.length >= 2) {\n const tip = segments[0];\n const next = segments[1];\n // For curves the tangent at t=0 is tip→cp1; for lines it's tip→next\n const fromX = next.type === 'C' ? next.cp1x! : next.x;\n const fromY = next.type === 'C' ? next.cp1y! : next.y;\n const angle = angleBetween(fromX, fromY, tip.x, tip.y);\n drawMarker(line, data.markerStart, tip.x, tip.y, angle, color);\n }\n if (data.markerEnd && data.markerEnd !== 'none' && segments.length >= 2) {\n const tip = segments[segments.length - 1];\n // For curves the tangent at t=1 is cp2→endpoint\n const fromX = tip.type === 'C' ? tip.cp2x! : segments[segments.length - 2].x;\n const fromY = tip.type === 'C' ? tip.cp2y! : segments[segments.length - 2].y;\n const angle = angleBetween(fromX, fromY, tip.x, tip.y);\n drawMarker(line, data.markerEnd, tip.x, tip.y, angle, color);\n }\n\n // Wider invisible hit area\n hitArea.clear();\n this._drawPath(hitArea, segments, '#000000', Math.max(width + 10, 14), 'solid');\n hitArea.alpha = 0.001;\n\n // Edge label\n if (data.labelText) {\n const center = getPathCenter(segments);\n if (!display.label) {\n // Background chip drawn first so it sits behind the text.\n const labelBg = new Graphics();\n labelBg.label = 'edge-label-bg';\n display.container.addChild(labelBg);\n display.labelBg = labelBg;\n\n const style = new TextStyle({\n fontFamily: data.labelFontFamily ?? 'system-ui, -apple-system, sans-serif',\n fontSize: data.labelFontSize ?? 12,\n fill: color,\n });\n display.label = new Text({ text: data.labelText, style });\n display.label.anchor.set(0.5, 0.5);\n display.container.addChild(display.label);\n }\n display.label.text = data.labelText;\n const lx = center.x;\n const ly = center.y - 10;\n display.label.position.set(lx, ly);\n (display.label.style as TextStyle).fill = color;\n\n // Draw a rounded background chip behind the label so the line / waypoints\n // don't show through the text.\n if (display.labelBg) {\n const padX = 5;\n const padY = 2;\n const tw = display.label.width + padX * 2;\n const th = display.label.height + padY * 2;\n display.labelBg.clear();\n display.labelBg\n .roundRect(lx - tw / 2, ly - th / 2, tw, th, 4)\n .fill({ color: 0xffffff, alpha: 0.92 });\n }\n } else {\n if (display.label) display.label.text = '';\n if (display.labelBg) display.labelBg.clear();\n }\n }\n\n private _drawPath(\n g: Graphics,\n segs: PathSegment[],\n color: string,\n width: number,\n lineStyle?: string,\n dashDensity?: string,\n ): void {\n const dash = getDashPattern(lineStyle, dashDensity, width);\n if (dash) {\n const offset = lineStyle === 'dynamic' ? this._dynamicOffset : 0;\n drawDashedPath(g, segs as PathSeg[], dash, offset);\n g.stroke({ color, width, cap: 'round' as const });\n } else {\n for (const seg of segs) {\n switch (seg.type) {\n case 'M': g.moveTo(seg.x, seg.y); break;\n case 'L': g.lineTo(seg.x, seg.y); break;\n case 'C': g.bezierCurveTo(seg.cp1x!, seg.cp1y!, seg.cp2x!, seg.cp2y!, seg.x, seg.y); break;\n case 'Q': g.quadraticCurveTo(seg.cp1x!, seg.cp1y!, seg.x, seg.y); break;\n }\n }\n g.stroke({ color, width });\n }\n }\n\n destroy(): void {\n for (const [, display] of this.edges) {\n display.container.destroy({ children: true });\n }\n this.edges.clear();\n this.edgeLayer.destroy({ children: true });\n }\n}\n"],"mappings":"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"}
|
|
@@ -45,15 +45,15 @@ function O(s) {
|
|
|
45
45
|
const a = [], e = (i) => {
|
|
46
46
|
const l = (i.data.label ?? "").trim();
|
|
47
47
|
return l ? l.replace(/\s+/g, " ") : i.type === "mediaNode" ? `(${i.data.mediaKind ?? "media"})` : `(${i.type})`;
|
|
48
|
-
}, n = (i, l,
|
|
48
|
+
}, n = (i, l, h) => {
|
|
49
49
|
const m = s.canvases[i];
|
|
50
50
|
if (!m) return;
|
|
51
|
-
const
|
|
51
|
+
const b = new Map(m.nodes.map((t) => [t.id, t])), o = /* @__PURE__ */ new Map();
|
|
52
52
|
for (const t of m.nodes) {
|
|
53
|
-
const r = t.parentId &&
|
|
53
|
+
const r = t.parentId && b.has(t.parentId) ? t.parentId : void 0, d = o.get(r) ?? [];
|
|
54
54
|
d.push(t), o.set(r, d);
|
|
55
55
|
}
|
|
56
|
-
const
|
|
56
|
+
const p = (t, r) => {
|
|
57
57
|
const d = " ".repeat(r), c = t.data.isGroup || t.type === "flowGroup" || t.type === "geometryGroup" ? `**${e(t)}**` : e(t);
|
|
58
58
|
if (a.push(`${d}- ${c}`), t.type === "mediaNode" && t.data.mediaKind) {
|
|
59
59
|
const g = t.data.mediaKind;
|
|
@@ -67,24 +67,24 @@ function O(s) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
const v = o.get(t.id) ?? [];
|
|
70
|
-
for (const g of v)
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
},
|
|
74
|
-
for (const t of
|
|
70
|
+
for (const g of v) p(g, r + 1);
|
|
71
|
+
const w = t.data.childCanvasId;
|
|
72
|
+
w && s.canvases[w] && !h.has(w) && (h.add(w), n(w, l + 1, h));
|
|
73
|
+
}, u = o.get(void 0) ?? [];
|
|
74
|
+
for (const t of u) p(t, 0);
|
|
75
75
|
if (m.edges.length > 0) {
|
|
76
76
|
a.push(""), a.push(`${"#".repeat(Math.min(6, l + 1))} 连接关系`);
|
|
77
77
|
for (const t of m.edges) {
|
|
78
|
-
const r =
|
|
78
|
+
const r = b.get(t.source), d = b.get(t.target);
|
|
79
79
|
if (!r || !d) continue;
|
|
80
80
|
const c = t.data.labelText ? ` (${t.data.labelText})` : "";
|
|
81
81
|
a.push(`- ${e(r)} → ${e(d)}${c}`);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
},
|
|
85
|
-
a.push("# 流程图"), a.push(""), n($, 1,
|
|
84
|
+
}, f = /* @__PURE__ */ new Set([$]);
|
|
85
|
+
a.push("# 流程图"), a.push(""), n($, 1, f);
|
|
86
86
|
for (const i of Object.keys(s.canvases))
|
|
87
|
-
|
|
87
|
+
f.has(i) || (f.add(i), a.push(""), a.push(`## 画布 ${i}`), a.push(""), n(i, 2, f));
|
|
88
88
|
return a.join(`
|
|
89
89
|
`).replace(/\n{3,}/g, `
|
|
90
90
|
|
|
@@ -94,16 +94,16 @@ function O(s) {
|
|
|
94
94
|
function R(s) {
|
|
95
95
|
const a = s.split(`
|
|
96
96
|
`).map((o) => o.trim()).filter((o) => o && !o.startsWith("%%")), e = /* @__PURE__ */ new Map(), n = [];
|
|
97
|
-
let
|
|
97
|
+
let f = !1;
|
|
98
98
|
for (const o of a) {
|
|
99
99
|
if (/^(graph|flowchart)\s+(LR|RL)/i.test(o)) {
|
|
100
|
-
|
|
100
|
+
f = !0;
|
|
101
101
|
continue;
|
|
102
102
|
}
|
|
103
103
|
if (/^(graph|flowchart)\s+/i.test(o)) continue;
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
const [, t, r, , d, c, v] =
|
|
104
|
+
const p = o.match(/^(\w+)(?:\[([^\]]*)\])?\s*(-->|---|-\.->|==>)(?:\|([^|]*)\|)?\s*(\w+)(?:\[([^\]]*)\])?/);
|
|
105
|
+
if (p) {
|
|
106
|
+
const [, t, r, , d, c, v] = p;
|
|
107
107
|
r && !e.has(t) && e.set(t, {
|
|
108
108
|
id: t,
|
|
109
109
|
label: r
|
|
@@ -123,45 +123,45 @@ function R(s) {
|
|
|
123
123
|
});
|
|
124
124
|
continue;
|
|
125
125
|
}
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
const [, t, r] =
|
|
126
|
+
const u = o.match(/^(\w+)\[([^\]]*)\]/);
|
|
127
|
+
if (u) {
|
|
128
|
+
const [, t, r] = u;
|
|
129
129
|
e.has(t) || e.set(t, {
|
|
130
130
|
id: t,
|
|
131
131
|
label: r
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
const i = [...e.keys()], l =
|
|
135
|
+
const i = [...e.keys()], l = f ? i.length : Math.ceil(Math.sqrt(i.length)), h = {
|
|
136
136
|
x: 200,
|
|
137
137
|
y: 120
|
|
138
|
-
}, m = i.map((o,
|
|
139
|
-
const
|
|
138
|
+
}, m = i.map((o, p) => {
|
|
139
|
+
const u = p % l, t = Math.floor(p / l), r = e.get(o);
|
|
140
140
|
return {
|
|
141
141
|
id: r.id,
|
|
142
142
|
type: "shapeRoundRect",
|
|
143
143
|
position: {
|
|
144
|
-
x: 100 +
|
|
145
|
-
y: 100 + t *
|
|
144
|
+
x: 100 + u * h.x,
|
|
145
|
+
y: 100 + t * h.y
|
|
146
146
|
},
|
|
147
147
|
width: 140,
|
|
148
148
|
height: 60,
|
|
149
149
|
data: {
|
|
150
150
|
label: r.label,
|
|
151
|
-
fill: "
|
|
152
|
-
stroke: "
|
|
151
|
+
fill: "theme:base-100",
|
|
152
|
+
stroke: "theme:base-content",
|
|
153
153
|
strokeWidth: 2,
|
|
154
|
-
color: "
|
|
154
|
+
color: "theme:base-content",
|
|
155
155
|
fontSize: 14
|
|
156
156
|
}
|
|
157
157
|
};
|
|
158
|
-
}),
|
|
158
|
+
}), b = n.map((o) => ({
|
|
159
159
|
id: N("edge"),
|
|
160
160
|
source: o.from,
|
|
161
161
|
target: o.to,
|
|
162
162
|
type: "smoothstep",
|
|
163
163
|
data: {
|
|
164
|
-
strokeColor: "
|
|
164
|
+
strokeColor: "theme:base-content",
|
|
165
165
|
strokeWidth: 2,
|
|
166
166
|
lineStyle: "solid",
|
|
167
167
|
markerEnd: "arrowclosed",
|
|
@@ -171,7 +171,7 @@ function R(s) {
|
|
|
171
171
|
return {
|
|
172
172
|
canvases: { [$]: {
|
|
173
173
|
nodes: m,
|
|
174
|
-
edges:
|
|
174
|
+
edges: b,
|
|
175
175
|
lines: []
|
|
176
176
|
} },
|
|
177
177
|
stack: [$]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFlowImportExport.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useFlowImportExport.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowDocument, FlowCanvasData, FlowNode, FlowEdge, FlowLine } from '../types';\nimport { createEmptyDocument, ROOT_CANVAS_ID } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\n\nconst PERSIST_VERSION = 1;\n\ninterface SerializedDocument {\n version: number;\n canvases: Record<string, FlowCanvasData>;\n stack: string[];\n title?: string;\n}\n\n/**\n * Sanitize a project title into a safe file base name, falling back to a\n * default when empty. Shared by all export paths so filenames stay consistent.\n */\nexport function safeFileBaseName(title: string | undefined, fallback = 'flowchart'): string {\n const name = (title ?? '').trim().replace(/[\\\\/:*?\"<>|]+/g, '-').replace(/\\s+/g, ' ').trim();\n return name || fallback;\n}\n\n/**\n * Serialize/deserialize FlowDocument to/from JSON.\n * Also includes Mermaid flowchart import.\n */\nexport function useFlowImportExport() {\n const { doc, setDoc } = useFlowContext();\n\n const exportJSON = useCallback((): string => {\n const serialized: SerializedDocument = {\n version: PERSIST_VERSION,\n canvases: doc.canvases,\n stack: doc.stack,\n title: doc.title,\n };\n return JSON.stringify(serialized, null, 2);\n }, [doc]);\n\n const importJSON = useCallback((json: string) => {\n try {\n const parsed = JSON.parse(json) as SerializedDocument;\n if (!parsed.canvases || !parsed.stack) throw new Error('Invalid document format');\n setDoc({\n canvases: parsed.canvases,\n stack: parsed.stack,\n title: parsed.title,\n });\n } catch (err) {\n console.error('Failed to import JSON:', err);\n }\n }, [setDoc]);\n\n const importMermaid = useCallback((mermaidCode: string) => {\n const doc = parseMermaidFlowchart(mermaidCode);\n setDoc(doc);\n }, [setDoc]);\n\n const exportMarkdown = useCallback((): string => {\n return documentToMarkdown(doc);\n }, [doc]);\n\n const clearDocument = useCallback(() => {\n setDoc(createEmptyDocument());\n }, [setDoc]);\n\n return { exportJSON, importJSON, importMermaid, exportMarkdown, clearDocument };\n}\n\n/**\n * Render a FlowDocument as a Markdown outline describing the node hierarchy\n * and connections. Groups become sections with their children nested beneath;\n * edges are listed as a connection summary per canvas. Sub-canvases (drill-down\n * nodes) are rendered as nested sections.\n */\nfunction documentToMarkdown(doc: FlowDocument): string {\n const lines: string[] = [];\n\n const nodeLabel = (n: FlowNode): string => {\n const raw = (n.data.label ?? '').trim();\n if (raw) return raw.replace(/\\s+/g, ' ');\n if (n.type === 'mediaNode') return `(${n.data.mediaKind ?? 'media'})`;\n return `(${n.type})`;\n };\n\n const renderCanvas = (canvasId: string, headingLevel: number, seenCanvases: Set<string>) => {\n const canvas = doc.canvases[canvasId];\n if (!canvas) return;\n\n // Index nodes by id and by parent for hierarchy traversal.\n const byId = new Map(canvas.nodes.map((n) => [n.id, n]));\n const childrenOf = new Map<string | undefined, FlowNode[]>();\n for (const n of canvas.nodes) {\n const key = n.parentId && byId.has(n.parentId) ? n.parentId : undefined;\n const list = childrenOf.get(key) ?? [];\n list.push(n);\n childrenOf.set(key, list);\n }\n\n const emitNode = (node: FlowNode, depth: number) => {\n const indent = ' '.repeat(depth);\n const isGroup = node.data.isGroup || node.type === 'flowGroup' || node.type === 'geometryGroup';\n const bullet = isGroup ? `**${nodeLabel(node)}**` : nodeLabel(node);\n lines.push(`${indent}- ${bullet}`);\n\n // Media content (markdown/data/etc.) as a short description line.\n if (node.type === 'mediaNode' && node.data.mediaKind) {\n const kind = node.data.mediaKind;\n if (kind === 'data' && node.data.dataConfig) {\n const cfg = node.data.dataConfig;\n const cols = cfg.columns.map((c) => c.label ?? c.key).join(', ');\n lines.push(`${indent} - 数据表:${cfg.rows.length} 行 · 列(${cols})`);\n } else if (node.data.mediaContent && kind !== 'image' && kind !== 'model3d') {\n const preview = node.data.mediaContent.trim().split('\\n')[0].slice(0, 80);\n if (preview) lines.push(`${indent} - ${kind}: ${preview}`);\n }\n }\n\n // Recurse into visual group children.\n const kids = childrenOf.get(node.id) ?? [];\n for (const kid of kids) emitNode(kid, depth + 1);\n\n // Recurse into a drill-down sub-canvas, if any.\n const childCanvasId = node.data.childCanvasId;\n if (childCanvasId && doc.canvases[childCanvasId] && !seenCanvases.has(childCanvasId)) {\n seenCanvases.add(childCanvasId);\n renderCanvas(childCanvasId, headingLevel + 1, seenCanvases);\n }\n };\n\n const roots = childrenOf.get(undefined) ?? [];\n for (const root of roots) emitNode(root, 0);\n\n // Connections summary.\n if (canvas.edges.length > 0) {\n lines.push('');\n lines.push(`${'#'.repeat(Math.min(6, headingLevel + 1))} 连接关系`);\n for (const edge of canvas.edges) {\n const from = byId.get(edge.source);\n const to = byId.get(edge.target);\n if (!from || !to) continue;\n const edgeLabel = edge.data.labelText ? ` (${edge.data.labelText})` : '';\n lines.push(`- ${nodeLabel(from)} → ${nodeLabel(to)}${edgeLabel}`);\n }\n }\n };\n\n const seen = new Set<string>([ROOT_CANVAS_ID]);\n lines.push('# 流程图');\n lines.push('');\n renderCanvas(ROOT_CANVAS_ID, 1, seen);\n\n // Any orphan canvases not reached via drill-down nodes.\n for (const canvasId of Object.keys(doc.canvases)) {\n if (seen.has(canvasId)) continue;\n seen.add(canvasId);\n lines.push('');\n lines.push(`## 画布 ${canvasId}`);\n lines.push('');\n renderCanvas(canvasId, 2, seen);\n }\n\n return lines.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim() + '\\n';\n}\n\n/**\n * Simple Mermaid flowchart parser.\n * Handles basic `graph TD` / `flowchart LR` syntax with node definitions and edges.\n */\nfunction parseMermaidFlowchart(code: string): FlowDocument {\n const lines = code.split('\\n').map(l => l.trim()).filter(l => l && !l.startsWith('%%'));\n\n const nodeMap = new Map<string, { id: string; label: string }>();\n const edges: Array<{ from: string; to: string; label?: string }> = [];\n\n let isHorizontal = false;\n\n for (const line of lines) {\n // Detect direction\n if (/^(graph|flowchart)\\s+(LR|RL)/i.test(line)) {\n isHorizontal = true;\n continue;\n }\n if (/^(graph|flowchart)\\s+/i.test(line)) continue;\n\n // Edge patterns: A --> B, A -->|label| B, A --- B\n const edgeMatch = line.match(/^(\\w+)(?:\\[([^\\]]*)\\])?\\s*(-->|---|-\\.->|==>)(?:\\|([^|]*)\\|)?\\s*(\\w+)(?:\\[([^\\]]*)\\])?/);\n if (edgeMatch) {\n const [, fromId, fromLabel, , edgeLabel, toId, toLabel] = edgeMatch;\n if (fromLabel && !nodeMap.has(fromId)) nodeMap.set(fromId, { id: fromId, label: fromLabel });\n if (toLabel && !nodeMap.has(toId)) nodeMap.set(toId, { id: toId, label: toLabel });\n if (!nodeMap.has(fromId)) nodeMap.set(fromId, { id: fromId, label: fromId });\n if (!nodeMap.has(toId)) nodeMap.set(toId, { id: toId, label: toId });\n edges.push({ from: fromId, to: toId, label: edgeLabel?.trim() });\n continue;\n }\n\n // Standalone node: A[Label]\n const nodeMatch = line.match(/^(\\w+)\\[([^\\]]*)\\]/);\n if (nodeMatch) {\n const [, id, label] = nodeMatch;\n if (!nodeMap.has(id)) nodeMap.set(id, { id, label });\n }\n }\n\n // Layout: simple grid placement\n const nodeIds = [...nodeMap.keys()];\n const cols = isHorizontal ? nodeIds.length : Math.ceil(Math.sqrt(nodeIds.length));\n const spacing = { x: 200, y: 120 };\n\n const nodes: FlowNode[] = nodeIds.map((id, i) => {\n const col = i % cols;\n const row = Math.floor(i / cols);\n const data = nodeMap.get(id)!;\n return {\n id: data.id,\n type: 'shapeRoundRect' as const,\n position: { x: 100 + col * spacing.x, y: 100 + row * spacing.y },\n width: 140,\n height: 60,\n data: {\n label: data.label,\n fill: '#ffffff',\n stroke: '#374151',\n strokeWidth: 2,\n color: '#111827',\n fontSize: 14,\n },\n };\n });\n\n const flowEdges: FlowEdge[] = edges.map((e) => ({\n id: generateId('edge'),\n source: e.from,\n target: e.to,\n type: 'smoothstep' as const,\n data: {\n strokeColor: '#374151',\n strokeWidth: 2,\n lineStyle: 'solid' as const,\n markerEnd: 'arrowclosed' as const,\n labelText: e.label,\n },\n }));\n\n return {\n canvases: {\n [ROOT_CANVAS_ID]: { nodes, edges: flowEdges, lines: [] },\n },\n stack: [ROOT_CANVAS_ID],\n };\n}\n\n/**\n * Export the PixiJS canvas as PNG data URL.\n */\nexport async function exportCanvasToPNG(canvas: HTMLCanvasElement): Promise<string> {\n return canvas.toDataURL('image/png');\n}\n\n/**\n * Download a data URL as a file.\n */\nexport function downloadDataURL(dataUrl: string, filename: string): void {\n const a = document.createElement('a');\n a.href = dataUrl;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useFlowImportExport.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useFlowImportExport.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport type { FlowDocument, FlowCanvasData, FlowNode, FlowEdge, FlowLine } from '../types';\nimport { createEmptyDocument, ROOT_CANVAS_ID } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\n\nconst PERSIST_VERSION = 1;\n\ninterface SerializedDocument {\n version: number;\n canvases: Record<string, FlowCanvasData>;\n stack: string[];\n title?: string;\n}\n\n/**\n * Sanitize a project title into a safe file base name, falling back to a\n * default when empty. Shared by all export paths so filenames stay consistent.\n */\nexport function safeFileBaseName(title: string | undefined, fallback = 'flowchart'): string {\n const name = (title ?? '').trim().replace(/[\\\\/:*?\"<>|]+/g, '-').replace(/\\s+/g, ' ').trim();\n return name || fallback;\n}\n\n/**\n * Serialize/deserialize FlowDocument to/from JSON.\n * Also includes Mermaid flowchart import.\n */\nexport function useFlowImportExport() {\n const { doc, setDoc } = useFlowContext();\n\n const exportJSON = useCallback((): string => {\n const serialized: SerializedDocument = {\n version: PERSIST_VERSION,\n canvases: doc.canvases,\n stack: doc.stack,\n title: doc.title,\n };\n return JSON.stringify(serialized, null, 2);\n }, [doc]);\n\n const importJSON = useCallback((json: string) => {\n try {\n const parsed = JSON.parse(json) as SerializedDocument;\n if (!parsed.canvases || !parsed.stack) throw new Error('Invalid document format');\n setDoc({\n canvases: parsed.canvases,\n stack: parsed.stack,\n title: parsed.title,\n });\n } catch (err) {\n console.error('Failed to import JSON:', err);\n }\n }, [setDoc]);\n\n const importMermaid = useCallback((mermaidCode: string) => {\n const doc = parseMermaidFlowchart(mermaidCode);\n setDoc(doc);\n }, [setDoc]);\n\n const exportMarkdown = useCallback((): string => {\n return documentToMarkdown(doc);\n }, [doc]);\n\n const clearDocument = useCallback(() => {\n setDoc(createEmptyDocument());\n }, [setDoc]);\n\n return { exportJSON, importJSON, importMermaid, exportMarkdown, clearDocument };\n}\n\n/**\n * Render a FlowDocument as a Markdown outline describing the node hierarchy\n * and connections. Groups become sections with their children nested beneath;\n * edges are listed as a connection summary per canvas. Sub-canvases (drill-down\n * nodes) are rendered as nested sections.\n */\nfunction documentToMarkdown(doc: FlowDocument): string {\n const lines: string[] = [];\n\n const nodeLabel = (n: FlowNode): string => {\n const raw = (n.data.label ?? '').trim();\n if (raw) return raw.replace(/\\s+/g, ' ');\n if (n.type === 'mediaNode') return `(${n.data.mediaKind ?? 'media'})`;\n return `(${n.type})`;\n };\n\n const renderCanvas = (canvasId: string, headingLevel: number, seenCanvases: Set<string>) => {\n const canvas = doc.canvases[canvasId];\n if (!canvas) return;\n\n // Index nodes by id and by parent for hierarchy traversal.\n const byId = new Map(canvas.nodes.map((n) => [n.id, n]));\n const childrenOf = new Map<string | undefined, FlowNode[]>();\n for (const n of canvas.nodes) {\n const key = n.parentId && byId.has(n.parentId) ? n.parentId : undefined;\n const list = childrenOf.get(key) ?? [];\n list.push(n);\n childrenOf.set(key, list);\n }\n\n const emitNode = (node: FlowNode, depth: number) => {\n const indent = ' '.repeat(depth);\n const isGroup = node.data.isGroup || node.type === 'flowGroup' || node.type === 'geometryGroup';\n const bullet = isGroup ? `**${nodeLabel(node)}**` : nodeLabel(node);\n lines.push(`${indent}- ${bullet}`);\n\n // Media content (markdown/data/etc.) as a short description line.\n if (node.type === 'mediaNode' && node.data.mediaKind) {\n const kind = node.data.mediaKind;\n if (kind === 'data' && node.data.dataConfig) {\n const cfg = node.data.dataConfig;\n const cols = cfg.columns.map((c) => c.label ?? c.key).join(', ');\n lines.push(`${indent} - 数据表:${cfg.rows.length} 行 · 列(${cols})`);\n } else if (node.data.mediaContent && kind !== 'image' && kind !== 'model3d') {\n const preview = node.data.mediaContent.trim().split('\\n')[0].slice(0, 80);\n if (preview) lines.push(`${indent} - ${kind}: ${preview}`);\n }\n }\n\n // Recurse into visual group children.\n const kids = childrenOf.get(node.id) ?? [];\n for (const kid of kids) emitNode(kid, depth + 1);\n\n // Recurse into a drill-down sub-canvas, if any.\n const childCanvasId = node.data.childCanvasId;\n if (childCanvasId && doc.canvases[childCanvasId] && !seenCanvases.has(childCanvasId)) {\n seenCanvases.add(childCanvasId);\n renderCanvas(childCanvasId, headingLevel + 1, seenCanvases);\n }\n };\n\n const roots = childrenOf.get(undefined) ?? [];\n for (const root of roots) emitNode(root, 0);\n\n // Connections summary.\n if (canvas.edges.length > 0) {\n lines.push('');\n lines.push(`${'#'.repeat(Math.min(6, headingLevel + 1))} 连接关系`);\n for (const edge of canvas.edges) {\n const from = byId.get(edge.source);\n const to = byId.get(edge.target);\n if (!from || !to) continue;\n const edgeLabel = edge.data.labelText ? ` (${edge.data.labelText})` : '';\n lines.push(`- ${nodeLabel(from)} → ${nodeLabel(to)}${edgeLabel}`);\n }\n }\n };\n\n const seen = new Set<string>([ROOT_CANVAS_ID]);\n lines.push('# 流程图');\n lines.push('');\n renderCanvas(ROOT_CANVAS_ID, 1, seen);\n\n // Any orphan canvases not reached via drill-down nodes.\n for (const canvasId of Object.keys(doc.canvases)) {\n if (seen.has(canvasId)) continue;\n seen.add(canvasId);\n lines.push('');\n lines.push(`## 画布 ${canvasId}`);\n lines.push('');\n renderCanvas(canvasId, 2, seen);\n }\n\n return lines.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trim() + '\\n';\n}\n\n/**\n * Simple Mermaid flowchart parser.\n * Handles basic `graph TD` / `flowchart LR` syntax with node definitions and edges.\n */\nfunction parseMermaidFlowchart(code: string): FlowDocument {\n const lines = code.split('\\n').map(l => l.trim()).filter(l => l && !l.startsWith('%%'));\n\n const nodeMap = new Map<string, { id: string; label: string }>();\n const edges: Array<{ from: string; to: string; label?: string }> = [];\n\n let isHorizontal = false;\n\n for (const line of lines) {\n // Detect direction\n if (/^(graph|flowchart)\\s+(LR|RL)/i.test(line)) {\n isHorizontal = true;\n continue;\n }\n if (/^(graph|flowchart)\\s+/i.test(line)) continue;\n\n // Edge patterns: A --> B, A -->|label| B, A --- B\n const edgeMatch = line.match(/^(\\w+)(?:\\[([^\\]]*)\\])?\\s*(-->|---|-\\.->|==>)(?:\\|([^|]*)\\|)?\\s*(\\w+)(?:\\[([^\\]]*)\\])?/);\n if (edgeMatch) {\n const [, fromId, fromLabel, , edgeLabel, toId, toLabel] = edgeMatch;\n if (fromLabel && !nodeMap.has(fromId)) nodeMap.set(fromId, { id: fromId, label: fromLabel });\n if (toLabel && !nodeMap.has(toId)) nodeMap.set(toId, { id: toId, label: toLabel });\n if (!nodeMap.has(fromId)) nodeMap.set(fromId, { id: fromId, label: fromId });\n if (!nodeMap.has(toId)) nodeMap.set(toId, { id: toId, label: toId });\n edges.push({ from: fromId, to: toId, label: edgeLabel?.trim() });\n continue;\n }\n\n // Standalone node: A[Label]\n const nodeMatch = line.match(/^(\\w+)\\[([^\\]]*)\\]/);\n if (nodeMatch) {\n const [, id, label] = nodeMatch;\n if (!nodeMap.has(id)) nodeMap.set(id, { id, label });\n }\n }\n\n // Layout: simple grid placement\n const nodeIds = [...nodeMap.keys()];\n const cols = isHorizontal ? nodeIds.length : Math.ceil(Math.sqrt(nodeIds.length));\n const spacing = { x: 200, y: 120 };\n\n const nodes: FlowNode[] = nodeIds.map((id, i) => {\n const col = i % cols;\n const row = Math.floor(i / cols);\n const data = nodeMap.get(id)!;\n return {\n id: data.id,\n type: 'shapeRoundRect' as const,\n position: { x: 100 + col * spacing.x, y: 100 + row * spacing.y },\n width: 140,\n height: 60,\n data: {\n label: data.label,\n fill: 'theme:base-100',\n stroke: 'theme:base-content',\n strokeWidth: 2,\n color: 'theme:base-content',\n fontSize: 14,\n },\n };\n });\n\n const flowEdges: FlowEdge[] = edges.map((e) => ({\n id: generateId('edge'),\n source: e.from,\n target: e.to,\n type: 'smoothstep' as const,\n data: {\n strokeColor: 'theme:base-content',\n strokeWidth: 2,\n lineStyle: 'solid' as const,\n markerEnd: 'arrowclosed' as const,\n labelText: e.label,\n },\n }));\n\n return {\n canvases: {\n [ROOT_CANVAS_ID]: { nodes, edges: flowEdges, lines: [] },\n },\n stack: [ROOT_CANVAS_ID],\n };\n}\n\n/**\n * Export the PixiJS canvas as PNG data URL.\n */\nexport async function exportCanvasToPNG(canvas: HTMLCanvasElement): Promise<string> {\n return canvas.toDataURL('image/png');\n}\n\n/**\n * Download a data URL as a file.\n */\nexport function downloadDataURL(dataUrl: string, filename: string): void {\n const a = document.createElement('a');\n a.href = dataUrl;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n}\n"],"mappings":"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"}
|