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,175 +1,192 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
1
|
+
import z from "../../../Markdown/Markdown.js";
|
|
2
|
+
import { resolveThemeColor as A, resolveThemeColorCss as D } from "../utils/themeColor.js";
|
|
3
|
+
import { streamAIChat as T } from "./aiClient.js";
|
|
4
|
+
import { Model3DView as $ } from "./Model3DView.js";
|
|
5
|
+
import H, { useEffect as v, useMemo as R, useRef as N, useState as f } from "react";
|
|
6
|
+
import { jsx as t, jsxs as b } from "react/jsx-runtime";
|
|
7
|
+
import V from "mermaid";
|
|
7
8
|
import "katex/dist/katex.min.css";
|
|
8
|
-
import
|
|
9
|
-
var
|
|
10
|
-
function
|
|
11
|
-
I || (
|
|
9
|
+
import K from "katex";
|
|
10
|
+
var L = H.lazy(() => import("./DataNodeView.js").then((e) => ({ default: e.DataNodeView }))), I = !1;
|
|
11
|
+
function _() {
|
|
12
|
+
I || (V.initialize({
|
|
12
13
|
startOnLoad: !1,
|
|
13
14
|
theme: "default",
|
|
14
15
|
securityLevel: "loose",
|
|
15
16
|
fontFamily: "inherit"
|
|
16
17
|
}), I = !0);
|
|
17
18
|
}
|
|
18
|
-
function
|
|
19
|
-
const [
|
|
20
|
-
return
|
|
21
|
-
let
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
25
|
-
|
|
19
|
+
function P({ content: e, color: r }) {
|
|
20
|
+
const [s, a] = f(""), [o, i] = f(null), u = N(`flow-mermaid-${Math.random().toString(36).slice(2)}`);
|
|
21
|
+
return v(() => {
|
|
22
|
+
let m = !1;
|
|
23
|
+
_();
|
|
24
|
+
const c = e.trim();
|
|
25
|
+
if (!c) {
|
|
26
|
+
a(""), i(null);
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
|
-
return
|
|
29
|
-
|
|
29
|
+
return V.render(u.current, c).then(({ svg: d }) => {
|
|
30
|
+
m || (a(d), i(null));
|
|
30
31
|
}).catch((d) => {
|
|
31
|
-
|
|
32
|
+
m || i(d?.message ?? "Mermaid error");
|
|
32
33
|
}), () => {
|
|
33
|
-
|
|
34
|
+
m = !0;
|
|
34
35
|
};
|
|
35
|
-
}, [
|
|
36
|
+
}, [e]), o ? /* @__PURE__ */ t("div", {
|
|
36
37
|
className: "flow-media-error",
|
|
37
|
-
children:
|
|
38
|
-
}) : /* @__PURE__ */
|
|
38
|
+
children: o
|
|
39
|
+
}) : /* @__PURE__ */ t("div", {
|
|
39
40
|
className: "flow-media-mermaid",
|
|
40
41
|
style: { color: r },
|
|
41
|
-
dangerouslySetInnerHTML: { __html:
|
|
42
|
+
dangerouslySetInnerHTML: { __html: s }
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
|
-
function
|
|
45
|
-
const
|
|
45
|
+
function j({ content: e, color: r, fontSize: s }) {
|
|
46
|
+
const a = R(() => {
|
|
46
47
|
try {
|
|
47
|
-
return
|
|
48
|
+
return K.renderToString(e.trim() || "\\;", {
|
|
48
49
|
displayMode: !0,
|
|
49
50
|
throwOnError: !1
|
|
50
51
|
});
|
|
51
|
-
} catch (
|
|
52
|
-
return `<span class="flow-media-error">${
|
|
52
|
+
} catch (o) {
|
|
53
|
+
return `<span class="flow-media-error">${o?.message ?? "KaTeX error"}</span>`;
|
|
53
54
|
}
|
|
54
|
-
}, [
|
|
55
|
-
return /* @__PURE__ */
|
|
55
|
+
}, [e]);
|
|
56
|
+
return /* @__PURE__ */ t("div", {
|
|
56
57
|
className: "flow-media-math",
|
|
57
58
|
style: {
|
|
58
59
|
color: r,
|
|
59
|
-
fontSize:
|
|
60
|
+
fontSize: s
|
|
60
61
|
},
|
|
61
|
-
dangerouslySetInnerHTML: { __html:
|
|
62
|
+
dangerouslySetInnerHTML: { __html: a }
|
|
62
63
|
});
|
|
63
64
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
65
|
+
function F({ content: e, interactive: r, color: s, fontSize: a }) {
|
|
66
|
+
const o = R(() => `<!doctype html><html><head><meta charset="utf-8"><style>
|
|
67
|
+
:root { color-scheme: light dark; }
|
|
68
|
+
html, body { margin: 0; background: transparent; }
|
|
69
|
+
body {
|
|
70
|
+
color: ${s ?? "#111827"};
|
|
71
|
+
font-size: ${a ?? 13}px;
|
|
72
|
+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
73
|
+
line-height: 1.5;
|
|
74
|
+
padding: 0;
|
|
75
|
+
}
|
|
76
|
+
a { color: inherit; }
|
|
77
|
+
</style></head><body>${e}</body></html>`, [
|
|
78
|
+
e,
|
|
79
|
+
s,
|
|
80
|
+
a
|
|
81
|
+
]);
|
|
82
|
+
return /* @__PURE__ */ t("iframe", {
|
|
66
83
|
title: "html-media",
|
|
67
84
|
className: "flow-media-iframe",
|
|
68
85
|
style: { pointerEvents: r ? "auto" : "none" },
|
|
69
86
|
sandbox: "allow-scripts allow-same-origin allow-popups allow-forms",
|
|
70
|
-
srcDoc:
|
|
87
|
+
srcDoc: o
|
|
71
88
|
});
|
|
72
89
|
}
|
|
73
|
-
function
|
|
74
|
-
const r =
|
|
75
|
-
return r ? /* @__PURE__ */
|
|
90
|
+
function O({ content: e }) {
|
|
91
|
+
const r = e.trim();
|
|
92
|
+
return r ? /* @__PURE__ */ t("img", {
|
|
76
93
|
className: "flow-media-image",
|
|
77
94
|
src: r,
|
|
78
95
|
alt: "",
|
|
79
96
|
draggable: !1,
|
|
80
97
|
referrerPolicy: "no-referrer"
|
|
81
|
-
}) : /* @__PURE__ */
|
|
98
|
+
}) : /* @__PURE__ */ t("div", {
|
|
82
99
|
className: "flow-media-image-empty",
|
|
83
100
|
children: "输入图片链接,或直接粘贴图片"
|
|
84
101
|
});
|
|
85
102
|
}
|
|
86
|
-
function
|
|
87
|
-
const [
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}, [
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
if (!
|
|
94
|
-
|
|
95
|
-
const p = [...
|
|
103
|
+
function q({ node: e, config: r, interactive: s, color: a }) {
|
|
104
|
+
const [o, i] = f([]), [u, m] = f(""), [c, d] = f(!1), [C, M] = f(null), w = N(null), y = N(null);
|
|
105
|
+
v(() => {
|
|
106
|
+
y.current?.scrollTo({ top: y.current.scrollHeight });
|
|
107
|
+
}, [o, c]), v(() => () => w.current?.abort(), []);
|
|
108
|
+
const S = !!r?.gateway && !!r?.model && !c, k = async () => {
|
|
109
|
+
const l = u.trim();
|
|
110
|
+
if (!l || !r?.gateway) return;
|
|
111
|
+
M(null);
|
|
112
|
+
const p = [...o.filter((n) => !(n.role === "assistant" && n.content.trim() === "")), {
|
|
96
113
|
role: "user",
|
|
97
|
-
content:
|
|
114
|
+
content: l
|
|
98
115
|
}];
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
let
|
|
103
|
-
|
|
116
|
+
i(p), m(""), d(!0);
|
|
117
|
+
const x = new AbortController();
|
|
118
|
+
w.current = x;
|
|
119
|
+
let E = "";
|
|
120
|
+
i((n) => [...n, {
|
|
104
121
|
role: "assistant",
|
|
105
122
|
content: ""
|
|
106
123
|
}]);
|
|
107
124
|
try {
|
|
108
|
-
for await (const
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
return
|
|
125
|
+
for await (const n of T(r, p, x.signal))
|
|
126
|
+
E += n, i((h) => {
|
|
127
|
+
const g = h.slice();
|
|
128
|
+
return g[g.length - 1] = {
|
|
112
129
|
role: "assistant",
|
|
113
|
-
content:
|
|
114
|
-
},
|
|
130
|
+
content: E
|
|
131
|
+
}, g;
|
|
115
132
|
});
|
|
116
|
-
} catch (
|
|
117
|
-
|
|
133
|
+
} catch (n) {
|
|
134
|
+
n?.name !== "AbortError" && M(n?.message ?? "Request failed");
|
|
118
135
|
} finally {
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
return
|
|
122
|
-
}), d(!1),
|
|
136
|
+
i((n) => {
|
|
137
|
+
const h = n[n.length - 1];
|
|
138
|
+
return h && h.role === "assistant" && h.content.trim() === "" ? n.slice(0, -1) : n;
|
|
139
|
+
}), d(!1), w.current = null;
|
|
123
140
|
}
|
|
124
141
|
};
|
|
125
|
-
return /* @__PURE__ */
|
|
142
|
+
return /* @__PURE__ */ b("div", {
|
|
126
143
|
className: "flow-media-ai",
|
|
127
|
-
style: { color:
|
|
144
|
+
style: { color: a },
|
|
128
145
|
children: [
|
|
129
|
-
/* @__PURE__ */
|
|
146
|
+
/* @__PURE__ */ t("div", {
|
|
130
147
|
className: "flow-media-ai-header",
|
|
131
|
-
children: r?.model ? `AI · ${r.model}` :
|
|
148
|
+
children: r?.model ? `AI · ${r.model}` : e.data.label || "AI"
|
|
132
149
|
}),
|
|
133
|
-
/* @__PURE__ */
|
|
150
|
+
/* @__PURE__ */ b("div", {
|
|
134
151
|
className: "flow-media-ai-log",
|
|
135
|
-
ref:
|
|
152
|
+
ref: y,
|
|
136
153
|
children: [
|
|
137
|
-
|
|
154
|
+
o.length === 0 && /* @__PURE__ */ t("div", {
|
|
138
155
|
className: "flow-media-ai-empty",
|
|
139
156
|
children: r?.gateway ? "开始对话…" : "请在属性面板配置 AI 接口"
|
|
140
157
|
}),
|
|
141
|
-
|
|
142
|
-
className: `flow-media-ai-msg flow-media-ai-msg--${
|
|
143
|
-
children:
|
|
144
|
-
content:
|
|
158
|
+
o.map((l, p) => /* @__PURE__ */ t("div", {
|
|
159
|
+
className: `flow-media-ai-msg flow-media-ai-msg--${l.role}`,
|
|
160
|
+
children: l.role === "assistant" ? /* @__PURE__ */ t(z, {
|
|
161
|
+
content: l.content || "…",
|
|
145
162
|
enableMermaid: !1,
|
|
146
163
|
enableMarkmap: !1
|
|
147
|
-
}) : /* @__PURE__ */
|
|
164
|
+
}) : /* @__PURE__ */ t("span", { children: l.content })
|
|
148
165
|
}, p)),
|
|
149
|
-
|
|
166
|
+
C && /* @__PURE__ */ t("div", {
|
|
150
167
|
className: "flow-media-error",
|
|
151
|
-
children:
|
|
168
|
+
children: C
|
|
152
169
|
})
|
|
153
170
|
]
|
|
154
171
|
}),
|
|
155
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ b("div", {
|
|
156
173
|
className: "flow-media-ai-input",
|
|
157
|
-
style: { pointerEvents:
|
|
158
|
-
children: [/* @__PURE__ */
|
|
159
|
-
value:
|
|
174
|
+
style: { pointerEvents: s ? "auto" : "none" },
|
|
175
|
+
children: [/* @__PURE__ */ t("textarea", {
|
|
176
|
+
value: u,
|
|
160
177
|
placeholder: "输入消息,Enter 发送",
|
|
161
|
-
onChange: (
|
|
162
|
-
onKeyDown: (
|
|
163
|
-
|
|
178
|
+
onChange: (l) => m(l.target.value),
|
|
179
|
+
onKeyDown: (l) => {
|
|
180
|
+
l.key === "Enter" && !l.shiftKey && (l.preventDefault(), S && k());
|
|
164
181
|
},
|
|
165
|
-
onPointerDown: (
|
|
166
|
-
}),
|
|
182
|
+
onPointerDown: (l) => l.stopPropagation()
|
|
183
|
+
}), c ? /* @__PURE__ */ t("button", {
|
|
167
184
|
type: "button",
|
|
168
|
-
onClick: () =>
|
|
185
|
+
onClick: () => w.current?.abort(),
|
|
169
186
|
children: "停止"
|
|
170
|
-
}) : /* @__PURE__ */
|
|
187
|
+
}) : /* @__PURE__ */ t("button", {
|
|
171
188
|
type: "button",
|
|
172
|
-
disabled: !
|
|
189
|
+
disabled: !S || !u.trim(),
|
|
173
190
|
onClick: k,
|
|
174
191
|
children: "发送"
|
|
175
192
|
})]
|
|
@@ -177,54 +194,56 @@ function F({ node: t, config: r, interactive: i, color: l }) {
|
|
|
177
194
|
]
|
|
178
195
|
});
|
|
179
196
|
}
|
|
180
|
-
function
|
|
181
|
-
const
|
|
182
|
-
switch (
|
|
197
|
+
function ee({ node: e, interactive: r }) {
|
|
198
|
+
const s = e.data.mediaKind ?? "markdown", a = e.data.mediaContent ?? "", o = D(e.data.labelColor ?? e.data.color, "#111827");
|
|
199
|
+
switch (s) {
|
|
183
200
|
case "markdown":
|
|
184
|
-
return /* @__PURE__ */
|
|
201
|
+
return /* @__PURE__ */ t("div", {
|
|
185
202
|
className: "flow-media-markdown",
|
|
186
203
|
style: {
|
|
187
|
-
color:
|
|
188
|
-
fontSize:
|
|
204
|
+
color: o,
|
|
205
|
+
fontSize: e.data.fontSize
|
|
189
206
|
},
|
|
190
|
-
children: /* @__PURE__ */
|
|
207
|
+
children: /* @__PURE__ */ t(z, { content: a })
|
|
191
208
|
});
|
|
192
209
|
case "html":
|
|
193
|
-
return /* @__PURE__ */
|
|
194
|
-
content:
|
|
195
|
-
interactive: r
|
|
210
|
+
return /* @__PURE__ */ t(F, {
|
|
211
|
+
content: a,
|
|
212
|
+
interactive: r,
|
|
213
|
+
color: A(e.data.labelColor ?? e.data.color, "#111827"),
|
|
214
|
+
fontSize: e.data.fontSize
|
|
196
215
|
});
|
|
197
216
|
case "mermaid":
|
|
198
|
-
return /* @__PURE__ */
|
|
199
|
-
content:
|
|
200
|
-
color:
|
|
217
|
+
return /* @__PURE__ */ t(P, {
|
|
218
|
+
content: a,
|
|
219
|
+
color: o
|
|
201
220
|
});
|
|
202
221
|
case "math":
|
|
203
|
-
return /* @__PURE__ */
|
|
204
|
-
content:
|
|
205
|
-
color:
|
|
206
|
-
fontSize:
|
|
222
|
+
return /* @__PURE__ */ t(j, {
|
|
223
|
+
content: a,
|
|
224
|
+
color: o,
|
|
225
|
+
fontSize: e.data.fontSize
|
|
207
226
|
});
|
|
208
227
|
case "image":
|
|
209
|
-
return /* @__PURE__ */
|
|
228
|
+
return /* @__PURE__ */ t(O, { content: a });
|
|
210
229
|
case "model3d":
|
|
211
|
-
return /* @__PURE__ */
|
|
212
|
-
content:
|
|
213
|
-
format:
|
|
230
|
+
return /* @__PURE__ */ t($, {
|
|
231
|
+
content: a,
|
|
232
|
+
format: e.data.modelFormat ?? "stl",
|
|
214
233
|
interactive: r
|
|
215
234
|
});
|
|
216
235
|
case "ai":
|
|
217
|
-
return /* @__PURE__ */
|
|
218
|
-
node:
|
|
219
|
-
config:
|
|
236
|
+
return /* @__PURE__ */ t(q, {
|
|
237
|
+
node: e,
|
|
238
|
+
config: e.data.aiConfig,
|
|
220
239
|
interactive: r,
|
|
221
|
-
color:
|
|
240
|
+
color: o
|
|
222
241
|
});
|
|
223
242
|
case "data":
|
|
224
|
-
return /* @__PURE__ */
|
|
243
|
+
return /* @__PURE__ */ t("div", {
|
|
225
244
|
className: "flow-media-data",
|
|
226
245
|
style: { pointerEvents: r ? "auto" : "none" },
|
|
227
|
-
children: /* @__PURE__ */
|
|
246
|
+
children: /* @__PURE__ */ t(L, { config: e.data.dataConfig ?? {
|
|
228
247
|
columns: [],
|
|
229
248
|
rows: [],
|
|
230
249
|
view: "table"
|
|
@@ -235,7 +254,7 @@ function W({ node: t, interactive: r }) {
|
|
|
235
254
|
}
|
|
236
255
|
}
|
|
237
256
|
export {
|
|
238
|
-
|
|
257
|
+
ee as MediaContent
|
|
239
258
|
};
|
|
240
259
|
|
|
241
260
|
//# sourceMappingURL=MediaContent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaContent.js","names":[],"sources":["../../../../../src/components/pro/Flow/nodes/MediaContent.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport katex from 'katex';\nimport mermaid from 'mermaid';\nimport Markdown from '../../../Markdown';\nimport type { FlowNode, FlowAIConfig } from '../types';\nimport { streamAIChat, type AIChatMessage } from './aiClient';\nimport 'katex/dist/katex.min.css';\nimport { Model3DView } from './Model3DView';\n\n// Data-node view pulls in DataTable + Chart; lazy-load so it only enters the\n// bundle when a data node actually renders.\nconst DataNodeView = React.lazy(() =>\n import('./DataNodeView').then((m) => ({ default: m.DataNodeView })),\n);\n\nlet mermaidReady = false;\nfunction ensureMermaid() {\n if (mermaidReady) return;\n mermaid.initialize({ startOnLoad: false, theme: 'default', securityLevel: 'loose', fontFamily: 'inherit' });\n mermaidReady = true;\n}\n\n/** Renders a Mermaid diagram from source into an inline SVG. */\nfunction MermaidView({ content, color }: { content: string; color?: string }) {\n const [svg, setSvg] = useState('');\n const [error, setError] = useState<string | null>(null);\n const idRef = useRef(`flow-mermaid-${Math.random().toString(36).slice(2)}`);\n\n useEffect(() => {\n let cancelled = false;\n ensureMermaid();\n const src = content.trim();\n if (!src) { setSvg(''); setError(null); return; }\n mermaid\n .render(idRef.current, src)\n .then(({ svg }) => { if (!cancelled) { setSvg(svg); setError(null); } })\n .catch((e) => { if (!cancelled) setError(e?.message ?? 'Mermaid error'); });\n return () => { cancelled = true; };\n }, [content]);\n\n if (error) return <div className=\"flow-media-error\">{error}</div>;\n return <div className=\"flow-media-mermaid\" style={{ color }} dangerouslySetInnerHTML={{ __html: svg }} />;\n}\n\n/** Renders a LaTeX formula (display mode) via KaTeX. */\nfunction MathView({ content, color, fontSize }: { content: string; color?: string; fontSize?: number }) {\n const html = useMemo(() => {\n try {\n return katex.renderToString(content.trim() || '\\\\;', { displayMode: true, throwOnError: false });\n } catch (e: any) {\n return `<span class=\"flow-media-error\">${e?.message ?? 'KaTeX error'}</span>`;\n }\n }, [content]);\n return <div className=\"flow-media-math\" style={{ color, fontSize }} dangerouslySetInnerHTML={{ __html: html }} />;\n}\n\n/** Renders raw HTML in a sandboxed iframe (isolated from the host document). */\nfunction HtmlView({ content, interactive }: { content: string; interactive: boolean }) {\n return (\n <iframe\n title=\"html-media\"\n className=\"flow-media-iframe\"\n style={{ pointerEvents: interactive ? 'auto' : 'none' }}\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-forms\"\n srcDoc={content}\n />\n );\n}\n\nfunction ImageView({ content }: { content: string }) {\n const src = content.trim();\n if (!src) {\n return <div className=\"flow-media-image-empty\">输入图片链接,或直接粘贴图片</div>;\n }\n return (\n <img\n className=\"flow-media-image\"\n src={src}\n alt=\"\"\n draggable={false}\n referrerPolicy=\"no-referrer\"\n />\n );\n}\n\n/** Interactive AI chat backed by an OpenAI/Anthropic-compatible endpoint. */\nfunction AIView({ node, config, interactive, color }: { node: FlowNode; config?: FlowAIConfig; interactive: boolean; color?: string }) {\n const [messages, setMessages] = useState<AIChatMessage[]>([]);\n const [input, setInput] = useState('');\n const [streaming, setStreaming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight });\n }, [messages, streaming]);\n\n useEffect(() => () => abortRef.current?.abort(), []);\n\n const canSend = !!config?.gateway && !!config?.model && !streaming;\n\n const send = async () => {\n const text = input.trim();\n if (!text || !config?.gateway) return;\n setError(null);\n // Only keep non-empty assistant messages in history; an aborted/errored\n // request can leave an empty assistant placeholder behind, which the API\n // then rejects (\"assistant message must not be empty\").\n const history = messages.filter(\n (m) => !(m.role === 'assistant' && m.content.trim() === ''),\n );\n const next: AIChatMessage[] = [...history, { role: 'user', content: text }];\n setMessages(next);\n setInput('');\n setStreaming(true);\n const ac = new AbortController();\n abortRef.current = ac;\n let acc = '';\n setMessages((m) => [...m, { role: 'assistant', content: '' }]);\n try {\n for await (const delta of streamAIChat(config, next, ac.signal)) {\n acc += delta;\n setMessages((m) => {\n const copy = m.slice();\n copy[copy.length - 1] = { role: 'assistant', content: acc };\n return copy;\n });\n }\n } catch (e: any) {\n if (e?.name !== 'AbortError') setError(e?.message ?? 'Request failed');\n } finally {\n // Drop the trailing assistant placeholder if nothing streamed in.\n setMessages((m) => {\n const last = m[m.length - 1];\n if (last && last.role === 'assistant' && last.content.trim() === '') {\n return m.slice(0, -1);\n }\n return m;\n });\n setStreaming(false);\n abortRef.current = null;\n }\n };\n\n return (\n <div className=\"flow-media-ai\" style={{ color }}>\n <div className=\"flow-media-ai-header\">\n {config?.model ? `AI · ${config.model}` : (node.data.label || 'AI')}\n </div>\n <div className=\"flow-media-ai-log\" ref={scrollRef}>\n {messages.length === 0 && (\n <div className=\"flow-media-ai-empty\">\n {config?.gateway ? '开始对话…' : '请在属性面板配置 AI 接口'}\n </div>\n )}\n {messages.map((m, i) => (\n <div key={i} className={`flow-media-ai-msg flow-media-ai-msg--${m.role}`}>\n {m.role === 'assistant'\n ? <Markdown content={m.content || '…'} enableMermaid={false} enableMarkmap={false} />\n : <span>{m.content}</span>}\n </div>\n ))}\n {error && <div className=\"flow-media-error\">{error}</div>}\n </div>\n <div className=\"flow-media-ai-input\" style={{ pointerEvents: interactive ? 'auto' : 'none' }}>\n <textarea\n value={input}\n placeholder=\"输入消息,Enter 发送\"\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); if (canSend) send(); }\n }}\n onPointerDown={(e) => e.stopPropagation()}\n />\n {streaming ? (\n <button type=\"button\" onClick={() => abortRef.current?.abort()}>停止</button>\n ) : (\n <button type=\"button\" disabled={!canSend || !input.trim()} onClick={send}>发送</button>\n )}\n </div>\n </div>\n );\n}\n\n/** Dispatches to the correct renderer for the node's media kind. */\nexport function MediaContent({ node, interactive }: { node: FlowNode; interactive: boolean }) {\n const kind = node.data.mediaKind ?? 'markdown';\n const content = node.data.mediaContent ?? '';\n const textColor = node.data.labelColor ?? node.data.color;\n\n switch (kind) {\n case 'markdown':\n return (\n <div className=\"flow-media-markdown\" style={{ color: textColor, fontSize: node.data.fontSize }}>\n <Markdown content={content} />\n </div>\n );\n case 'html':\n return <HtmlView content={content} interactive={interactive} />;\n case 'mermaid':\n return <MermaidView content={content} color={textColor} />;\n case 'math':\n return <MathView content={content} color={textColor} fontSize={node.data.fontSize} />;\n case 'image':\n return <ImageView content={content} />;\n case 'model3d':\n return (\n <Model3DView\n content={content}\n format={node.data.modelFormat ?? 'stl'}\n interactive={interactive}\n />\n );\n case 'ai':\n return <AIView node={node} config={node.data.aiConfig} interactive={interactive} color={textColor} />;\n case 'data':\n return (\n <div className=\"flow-media-data\" style={{ pointerEvents: interactive ? 'auto' : 'none' }}>\n <DataNodeView config={node.data.dataConfig ?? { columns: [], rows: [], view: 'table' }} />\n </div>\n );\n default:\n return null;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MediaContent.js","names":[],"sources":["../../../../../src/components/pro/Flow/nodes/MediaContent.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport katex from 'katex';\nimport mermaid from 'mermaid';\nimport Markdown from '../../../Markdown';\nimport type { FlowNode, FlowAIConfig } from '../types';\nimport { streamAIChat, type AIChatMessage } from './aiClient';\nimport { resolveThemeColorCss, resolveThemeColor } from '../utils/themeColor';\nimport 'katex/dist/katex.min.css';\nimport { Model3DView } from './Model3DView';\n\n// Data-node view pulls in DataTable + Chart; lazy-load so it only enters the\n// bundle when a data node actually renders.\nconst DataNodeView = React.lazy(() =>\n import('./DataNodeView').then((m) => ({ default: m.DataNodeView })),\n);\n\nlet mermaidReady = false;\nfunction ensureMermaid() {\n if (mermaidReady) return;\n mermaid.initialize({ startOnLoad: false, theme: 'default', securityLevel: 'loose', fontFamily: 'inherit' });\n mermaidReady = true;\n}\n\n/** Renders a Mermaid diagram from source into an inline SVG. */\nfunction MermaidView({ content, color }: { content: string; color?: string }) {\n const [svg, setSvg] = useState('');\n const [error, setError] = useState<string | null>(null);\n const idRef = useRef(`flow-mermaid-${Math.random().toString(36).slice(2)}`);\n\n useEffect(() => {\n let cancelled = false;\n ensureMermaid();\n const src = content.trim();\n if (!src) { setSvg(''); setError(null); return; }\n mermaid\n .render(idRef.current, src)\n .then(({ svg }) => { if (!cancelled) { setSvg(svg); setError(null); } })\n .catch((e) => { if (!cancelled) setError(e?.message ?? 'Mermaid error'); });\n return () => { cancelled = true; };\n }, [content]);\n\n if (error) return <div className=\"flow-media-error\">{error}</div>;\n return <div className=\"flow-media-mermaid\" style={{ color }} dangerouslySetInnerHTML={{ __html: svg }} />;\n}\n\n/** Renders a LaTeX formula (display mode) via KaTeX. */\nfunction MathView({ content, color, fontSize }: { content: string; color?: string; fontSize?: number }) {\n const html = useMemo(() => {\n try {\n return katex.renderToString(content.trim() || '\\\\;', { displayMode: true, throwOnError: false });\n } catch (e: any) {\n return `<span class=\"flow-media-error\">${e?.message ?? 'KaTeX error'}</span>`;\n }\n }, [content]);\n return <div className=\"flow-media-math\" style={{ color, fontSize }} dangerouslySetInnerHTML={{ __html: html }} />;\n}\n\n/** Renders raw HTML in a sandboxed iframe (isolated from the host document).\n * A base stylesheet is injected so the fragment inherits the node's themed\n * text color / font size and a transparent background, keeping it readable on\n * any theme without the author having to set colors. */\nfunction HtmlView({ content, interactive, color, fontSize }: { content: string; interactive: boolean; color?: string; fontSize?: number }) {\n const srcDoc = useMemo(() => {\n const base = `<!doctype html><html><head><meta charset=\"utf-8\"><style>\n :root { color-scheme: light dark; }\n html, body { margin: 0; background: transparent; }\n body {\n color: ${color ?? '#111827'};\n font-size: ${fontSize ?? 13}px;\n font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n line-height: 1.5;\n padding: 0;\n }\n a { color: inherit; }\n </style></head><body>${content}</body></html>`;\n return base;\n }, [content, color, fontSize]);\n return (\n <iframe\n title=\"html-media\"\n className=\"flow-media-iframe\"\n style={{ pointerEvents: interactive ? 'auto' : 'none' }}\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-forms\"\n srcDoc={srcDoc}\n />\n );\n}\n\nfunction ImageView({ content }: { content: string }) {\n const src = content.trim();\n if (!src) {\n return <div className=\"flow-media-image-empty\">输入图片链接,或直接粘贴图片</div>;\n }\n return (\n <img\n className=\"flow-media-image\"\n src={src}\n alt=\"\"\n draggable={false}\n referrerPolicy=\"no-referrer\"\n />\n );\n}\n\n/** Interactive AI chat backed by an OpenAI/Anthropic-compatible endpoint. */\nfunction AIView({ node, config, interactive, color }: { node: FlowNode; config?: FlowAIConfig; interactive: boolean; color?: string }) {\n const [messages, setMessages] = useState<AIChatMessage[]>([]);\n const [input, setInput] = useState('');\n const [streaming, setStreaming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight });\n }, [messages, streaming]);\n\n useEffect(() => () => abortRef.current?.abort(), []);\n\n const canSend = !!config?.gateway && !!config?.model && !streaming;\n\n const send = async () => {\n const text = input.trim();\n if (!text || !config?.gateway) return;\n setError(null);\n // Only keep non-empty assistant messages in history; an aborted/errored\n // request can leave an empty assistant placeholder behind, which the API\n // then rejects (\"assistant message must not be empty\").\n const history = messages.filter(\n (m) => !(m.role === 'assistant' && m.content.trim() === ''),\n );\n const next: AIChatMessage[] = [...history, { role: 'user', content: text }];\n setMessages(next);\n setInput('');\n setStreaming(true);\n const ac = new AbortController();\n abortRef.current = ac;\n let acc = '';\n setMessages((m) => [...m, { role: 'assistant', content: '' }]);\n try {\n for await (const delta of streamAIChat(config, next, ac.signal)) {\n acc += delta;\n setMessages((m) => {\n const copy = m.slice();\n copy[copy.length - 1] = { role: 'assistant', content: acc };\n return copy;\n });\n }\n } catch (e: any) {\n if (e?.name !== 'AbortError') setError(e?.message ?? 'Request failed');\n } finally {\n // Drop the trailing assistant placeholder if nothing streamed in.\n setMessages((m) => {\n const last = m[m.length - 1];\n if (last && last.role === 'assistant' && last.content.trim() === '') {\n return m.slice(0, -1);\n }\n return m;\n });\n setStreaming(false);\n abortRef.current = null;\n }\n };\n\n return (\n <div className=\"flow-media-ai\" style={{ color }}>\n <div className=\"flow-media-ai-header\">\n {config?.model ? `AI · ${config.model}` : (node.data.label || 'AI')}\n </div>\n <div className=\"flow-media-ai-log\" ref={scrollRef}>\n {messages.length === 0 && (\n <div className=\"flow-media-ai-empty\">\n {config?.gateway ? '开始对话…' : '请在属性面板配置 AI 接口'}\n </div>\n )}\n {messages.map((m, i) => (\n <div key={i} className={`flow-media-ai-msg flow-media-ai-msg--${m.role}`}>\n {m.role === 'assistant'\n ? <Markdown content={m.content || '…'} enableMermaid={false} enableMarkmap={false} />\n : <span>{m.content}</span>}\n </div>\n ))}\n {error && <div className=\"flow-media-error\">{error}</div>}\n </div>\n <div className=\"flow-media-ai-input\" style={{ pointerEvents: interactive ? 'auto' : 'none' }}>\n <textarea\n value={input}\n placeholder=\"输入消息,Enter 发送\"\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); if (canSend) send(); }\n }}\n onPointerDown={(e) => e.stopPropagation()}\n />\n {streaming ? (\n <button type=\"button\" onClick={() => abortRef.current?.abort()}>停止</button>\n ) : (\n <button type=\"button\" disabled={!canSend || !input.trim()} onClick={send}>发送</button>\n )}\n </div>\n </div>\n );\n}\n\n/** Dispatches to the correct renderer for the node's media kind. */\nexport function MediaContent({ node, interactive }: { node: FlowNode; interactive: boolean }) {\n const kind = node.data.mediaKind ?? 'markdown';\n const content = node.data.mediaContent ?? '';\n const textColor = resolveThemeColorCss(node.data.labelColor ?? node.data.color, '#111827');\n\n switch (kind) {\n case 'markdown':\n return (\n <div className=\"flow-media-markdown\" style={{ color: textColor, fontSize: node.data.fontSize }}>\n <Markdown content={content} />\n </div>\n );\n case 'html':\n // The iframe is an isolated document with no access to the parent's CSS\n // variables, so pass a concrete resolved color instead of a var().\n return (\n <HtmlView\n content={content}\n interactive={interactive}\n color={resolveThemeColor(node.data.labelColor ?? node.data.color, '#111827')}\n fontSize={node.data.fontSize}\n />\n );\n case 'mermaid':\n return <MermaidView content={content} color={textColor} />;\n case 'math':\n return <MathView content={content} color={textColor} fontSize={node.data.fontSize} />;\n case 'image':\n return <ImageView content={content} />;\n case 'model3d':\n return (\n <Model3DView\n content={content}\n format={node.data.modelFormat ?? 'stl'}\n interactive={interactive}\n />\n );\n case 'ai':\n return <AIView node={node} config={node.data.aiConfig} interactive={interactive} color={textColor} />;\n case 'data':\n return (\n <div className=\"flow-media-data\" style={{ pointerEvents: interactive ? 'auto' : 'none' }}>\n <DataNodeView config={node.data.dataConfig ?? { columns: [], rows: [], view: 'table' }} />\n </div>\n );\n default:\n return null;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { useFlowContext as
|
|
2
|
-
import { useFlowUI as
|
|
3
|
-
import
|
|
1
|
+
import { useFlowContext as w } from "../context/FlowContext.js";
|
|
2
|
+
import { useFlowUI as y } from "../context/FlowUIContext.js";
|
|
3
|
+
import { resolveThemeColorCss as s } from "../utils/themeColor.js";
|
|
4
|
+
import i, { Suspense as C, useMemo as x } from "react";
|
|
4
5
|
import { jsx as r } from "react/jsx-runtime";
|
|
5
|
-
var
|
|
6
|
+
var N = i.lazy(() => import("./MediaContent.js").then((t) => ({ default: t.MediaContent }))), M = class extends i.Component {
|
|
6
7
|
constructor(t) {
|
|
7
8
|
super(t), this.state = { error: null };
|
|
8
9
|
}
|
|
@@ -16,21 +17,21 @@ var y = s.lazy(() => import("./MediaContent.js").then((t) => ({ default: t.Media
|
|
|
16
17
|
}) : this.props.children;
|
|
17
18
|
}
|
|
18
19
|
};
|
|
19
|
-
function
|
|
20
|
-
const { currentCanvas: t, selection:
|
|
21
|
-
if (
|
|
22
|
-
const { x:
|
|
20
|
+
function D() {
|
|
21
|
+
const { currentCanvas: t, selection: n } = w(), { viewport: l, isNodeDragging: d } = y(), a = x(() => t.nodes.filter((e) => e.type === "mediaNode"), [t.nodes]);
|
|
22
|
+
if (a.length === 0) return null;
|
|
23
|
+
const { x: c, y: m, zoom: o } = l;
|
|
23
24
|
return /* @__PURE__ */ r("div", {
|
|
24
25
|
className: "flow-media-layer",
|
|
25
|
-
children:
|
|
26
|
-
const
|
|
26
|
+
children: a.map((e) => {
|
|
27
|
+
const p = e.position.x * o + c, f = e.position.y * o + m, h = n.nodeIds.has(e.id) && !d, u = e.data.fill === "transparent" ? "transparent" : s(e.data.fill, "transparent"), v = s(e.data.labelColor ?? e.data.color, "#111827");
|
|
27
28
|
return /* @__PURE__ */ r("div", {
|
|
28
29
|
className: "flow-media-node",
|
|
29
30
|
"data-media-node": e.id,
|
|
30
31
|
style: {
|
|
31
32
|
position: "absolute",
|
|
32
|
-
left:
|
|
33
|
-
top:
|
|
33
|
+
left: p,
|
|
34
|
+
top: f,
|
|
34
35
|
width: e.width,
|
|
35
36
|
height: e.height,
|
|
36
37
|
transform: `scale(${o})`,
|
|
@@ -39,15 +40,19 @@ function F() {
|
|
|
39
40
|
},
|
|
40
41
|
children: /* @__PURE__ */ r("div", {
|
|
41
42
|
className: "flow-media-surface",
|
|
42
|
-
onWheel: (
|
|
43
|
-
|
|
43
|
+
onWheel: (g) => g.stopPropagation(),
|
|
44
|
+
style: {
|
|
45
|
+
background: u,
|
|
46
|
+
color: v
|
|
47
|
+
},
|
|
48
|
+
children: /* @__PURE__ */ r(C, {
|
|
44
49
|
fallback: /* @__PURE__ */ r("div", {
|
|
45
50
|
className: "flow-media-loading",
|
|
46
51
|
children: "…"
|
|
47
52
|
}),
|
|
48
|
-
children: /* @__PURE__ */ r(
|
|
53
|
+
children: /* @__PURE__ */ r(M, { children: /* @__PURE__ */ r(N, {
|
|
49
54
|
node: e,
|
|
50
|
-
interactive:
|
|
55
|
+
interactive: h
|
|
51
56
|
}) })
|
|
52
57
|
})
|
|
53
58
|
})
|
|
@@ -56,7 +61,7 @@ function F() {
|
|
|
56
61
|
});
|
|
57
62
|
}
|
|
58
63
|
export {
|
|
59
|
-
|
|
64
|
+
D as MediaOverlay
|
|
60
65
|
};
|
|
61
66
|
|
|
62
67
|
//# sourceMappingURL=MediaOverlay.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaOverlay.js","names":[],"sources":["../../../../../src/components/pro/Flow/nodes/MediaOverlay.tsx"],"sourcesContent":["import React, { useMemo, Suspense } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\n\n// Lazy-load the media renderers so the heavy optional deps they pull in\n// (react-markdown, mermaid, katex, markmap, …) are only fetched when a media\n// node actually needs to render. This keeps the base `dishui/flow` bundle\n// lightweight and avoids loading that stack for diagrams without media nodes.\nconst MediaContent = React.lazy(() =>\n import('./MediaContent').then((m) => ({ default: m.MediaContent })),\n);\n\n/** Isolates media render/load failures so one bad node never blanks the editor. */\nclass MediaErrorBoundary extends React.Component<\n { children: React.ReactNode },\n { error: Error | null }\n> {\n constructor(props: { children: React.ReactNode }) {\n super(props);\n this.state = { error: null };\n }\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n render() {\n if (this.state.error) {\n return <div className=\"flow-media-error\">{this.state.error.message}</div>;\n }\n return this.props.children;\n }\n}\n\n/**\n * DOM overlay for media nodes. PixiJS draws the frame; this layer renders the\n * live content (markdown/html/mermaid/math/AI) on top, synced to the viewport\n * so it pans and zooms with the canvas.\n *\n * Event model: a media surface is `pointer-events: none` by default so the\n * canvas underneath can be clicked to select/drag the node's frame. The\n * surface only becomes interactive while its node is selected (so AI chat can\n * be typed into, HTML can be scrolled). This also stops an always-interactive\n * iframe/AI box from swallowing pointer gestures and leaving the drag stuck.\n */\nexport function MediaOverlay() {\n const { currentCanvas, selection } = useFlowContext();\n const { viewport, isNodeDragging } = useFlowUI();\n\n const mediaNodes = useMemo(\n () => currentCanvas.nodes.filter((n) => n.type === 'mediaNode'),\n [currentCanvas.nodes],\n );\n\n if (mediaNodes.length === 0) return null;\n\n const { x: vx, y: vy, zoom } = viewport;\n\n return (\n <div className=\"flow-media-layer\">\n {mediaNodes.map((node) => {\n // World → screen (CSS px, matching ViewportManager.worldToScreen).\n const left = node.position.x * zoom + vx;\n const top = node.position.y * zoom + vy;\n // Interactive only while selected, so the canvas stays draggable.\n // Suppress entirely during a drag: an interactive iframe would swallow\n // the pointer and the canvas would never receive pointerup (stuck drag).\n const interactive = selection.nodeIds.has(node.id) && !isNodeDragging;\n return (\n <div\n key={node.id}\n className=\"flow-media-node\"\n data-media-node={node.id}\n style={{\n position: 'absolute',\n left,\n top,\n width: node.width,\n height: node.height,\n // Scale the fixed-size content box to match zoom without\n // reflowing text on every zoom step.\n transform: `scale(${zoom})`,\n transformOrigin: 'top left',\n // Below the frame's selection outline but above the canvas.\n pointerEvents: 'none',\n }}\n >\n <div\n className=\"flow-media-surface\"\n onWheel={(e) => e.stopPropagation()}\n >\n <Suspense fallback={<div className=\"flow-media-loading\">…</div>}>\n <MediaErrorBoundary>\n <MediaContent node={node} interactive={interactive} />\n </MediaErrorBoundary>\n </Suspense>\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"MediaOverlay.js","names":[],"sources":["../../../../../src/components/pro/Flow/nodes/MediaOverlay.tsx"],"sourcesContent":["import React, { useMemo, Suspense } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { resolveThemeColorCss } from '../utils/themeColor';\n\n// Lazy-load the media renderers so the heavy optional deps they pull in\n// (react-markdown, mermaid, katex, markmap, …) are only fetched when a media\n// node actually needs to render. This keeps the base `dishui/flow` bundle\n// lightweight and avoids loading that stack for diagrams without media nodes.\nconst MediaContent = React.lazy(() =>\n import('./MediaContent').then((m) => ({ default: m.MediaContent })),\n);\n\n/** Isolates media render/load failures so one bad node never blanks the editor. */\nclass MediaErrorBoundary extends React.Component<\n { children: React.ReactNode },\n { error: Error | null }\n> {\n constructor(props: { children: React.ReactNode }) {\n super(props);\n this.state = { error: null };\n }\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n render() {\n if (this.state.error) {\n return <div className=\"flow-media-error\">{this.state.error.message}</div>;\n }\n return this.props.children;\n }\n}\n\n/**\n * DOM overlay for media nodes. PixiJS draws the frame; this layer renders the\n * live content (markdown/html/mermaid/math/AI) on top, synced to the viewport\n * so it pans and zooms with the canvas.\n *\n * Event model: a media surface is `pointer-events: none` by default so the\n * canvas underneath can be clicked to select/drag the node's frame. The\n * surface only becomes interactive while its node is selected (so AI chat can\n * be typed into, HTML can be scrolled). This also stops an always-interactive\n * iframe/AI box from swallowing pointer gestures and leaving the drag stuck.\n */\nexport function MediaOverlay() {\n const { currentCanvas, selection } = useFlowContext();\n const { viewport, isNodeDragging } = useFlowUI();\n\n const mediaNodes = useMemo(\n () => currentCanvas.nodes.filter((n) => n.type === 'mediaNode'),\n [currentCanvas.nodes],\n );\n\n if (mediaNodes.length === 0) return null;\n\n const { x: vx, y: vy, zoom } = viewport;\n\n return (\n <div className=\"flow-media-layer\">\n {mediaNodes.map((node) => {\n // World → screen (CSS px, matching ViewportManager.worldToScreen).\n const left = node.position.x * zoom + vx;\n const top = node.position.y * zoom + vy;\n // Interactive only while selected, so the canvas stays draggable.\n // Suppress entirely during a drag: an interactive iframe would swallow\n // the pointer and the canvas would never receive pointerup (stuck drag).\n const interactive = selection.nodeIds.has(node.id) && !isNodeDragging;\n // Drive the overlay's background / text from the node's own color data.\n // Theme tokens (`theme:*`) resolve to CSS variables so the media content\n // follows theme switches natively; literal colors are used as-is. This\n // keeps html / ai / data (which render opaque content) in sync with the\n // fill/color chosen in the inspector, matching the PixiJS frame drawn\n // underneath. The border is owned by the Pixi frame (also what PNG/PDF\n // export captures), so we don't redraw it here.\n const fillCss = node.data.fill === 'transparent'\n ? 'transparent'\n : resolveThemeColorCss(node.data.fill, 'transparent');\n const textColor = resolveThemeColorCss(node.data.labelColor ?? node.data.color, '#111827');\n return (\n <div\n key={node.id}\n className=\"flow-media-node\"\n data-media-node={node.id}\n style={{\n position: 'absolute',\n left,\n top,\n width: node.width,\n height: node.height,\n // Scale the fixed-size content box to match zoom without\n // reflowing text on every zoom step.\n transform: `scale(${zoom})`,\n transformOrigin: 'top left',\n // Below the frame's selection outline but above the canvas.\n pointerEvents: 'none',\n }}\n >\n <div\n className=\"flow-media-surface\"\n onWheel={(e) => e.stopPropagation()}\n style={{\n background: fillCss,\n color: textColor,\n }}\n >\n <Suspense fallback={<div className=\"flow-media-loading\">…</div>}>\n <MediaErrorBoundary>\n <MediaContent node={node} interactive={interactive} />\n </MediaErrorBoundary>\n </Suspense>\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;AASA,IAAM,IAAe,EAAM,KAAA,MACzB,OAAO,mBAAA,EAAkB,KAAA,CAAM,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CACpE,GAGM,IAAN,cAAiC,EAAM,UAGrC;AAAA,EACA,YAAY,GAAsC;AAChD,UAAM,CAAK,GACX,KAAK,QAAQ,EAAE,OAAO,KAAK;AAAA,EAC7B;AAAA,EACA,OAAO,yBAAyB,GAAc;AAC5C,WAAO,EAAE,OAAA,EAAM;AAAA,EACjB;AAAA,EACA,SAAS;AACP,WAAI,KAAK,MAAM,QACN,gBAAA,EAAC,OAAD;AAAA,MAAK,WAAU;AAAA,gBAAoB,KAAK,MAAM,MAAM;AAAA,IAAa,CAAA,IAEnE,KAAK,MAAM;AAAA,EACpB;AACF;AAaA,SAAgB,IAAe;AAC7B,QAAM,EAAE,eAAA,GAAe,WAAA,EAAA,IAAc,EAAe,GAC9C,EAAE,UAAA,GAAU,gBAAA,EAAA,IAAmB,EAAU,GAEzC,IAAa,EAAA,MACX,EAAc,MAAM,OAAA,CAAQ,MAAM,EAAE,SAAS,WAAW,GAC9D,CAAC,EAAc,KAAK,CACtB;AAEA,MAAI,EAAW,WAAW,EAAG,QAAO;AAEpC,QAAM,EAAE,GAAG,GAAI,GAAG,GAAI,MAAA,EAAA,IAAS;AAE/B,SACE,gBAAA,EAAC,OAAD;AAAA,IAAK,WAAU;AAAA,cACZ,EAAW,IAAA,CAAK,MAAS;AAExB,YAAM,IAAO,EAAK,SAAS,IAAI,IAAO,GAChC,IAAM,EAAK,SAAS,IAAI,IAAO,GAI/B,IAAc,EAAU,QAAQ,IAAI,EAAK,EAAE,KAAK,CAAC,GAQjD,IAAU,EAAK,KAAK,SAAS,gBAC/B,gBACA,EAAqB,EAAK,KAAK,MAAM,aAAa,GAChD,IAAY,EAAqB,EAAK,KAAK,cAAc,EAAK,KAAK,OAAO,SAAS;AACzF,aACE,gBAAA,EAAC,OAAD;AAAA,QAEE,WAAU;AAAA,QACV,mBAAiB,EAAK;AAAA,QACtB,OAAO;AAAA,UACL,UAAU;AAAA,UACV,MAAA;AAAA,UACA,KAAA;AAAA,UACA,OAAO,EAAK;AAAA,UACZ,QAAQ,EAAK;AAAA,UAGb,WAAW,SAAS,CAAA;AAAA,UACpB,iBAAiB;AAAA,UAEjB,eAAe;AAAA,QACjB;AAAA,kBAEA,gBAAA,EAAC,OAAD;AAAA,UACE,WAAU;AAAA,UACV,SAAA,CAAU,MAAM,EAAE,gBAAgB;AAAA,UAClC,OAAO;AAAA,YACL,YAAY;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,oBAEA,gBAAA,EAAC,GAAD;AAAA,YAAU,UAAU,gBAAA,EAAC,OAAD;AAAA,cAAK,WAAU;AAAA,wBAAqB;AAAA,YAAM,CAAA;AAAA,sBAC5D,gBAAA,EAAC,GAAD,EAAA,UACE,gBAAA,EAAC,GAAD;AAAA,cAAoB,MAAA;AAAA,cAAmB,aAAA;AAAA,YAAc,CAAA,EACnC,CAAA;AAAA,UACZ,CAAA;AAAA,QACP,CAAA;AAAA,MACF,GA/BE,EAAK,EA+BP;AAAA,IAET,CAAC;AAAA,EACE,CAAA;AAET"}
|