@langgraph-js/ui 1.2.0 → 1.3.0
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/LICENSE +201 -201
- package/README.md +6 -6
- package/cli.mjs +36 -36
- package/dist/assets/index-88Ds6iYK.js +214 -0
- package/dist/assets/index-_qsGvf6N.css +1 -0
- package/dist/index.html +14 -14
- package/index.html +22 -22
- package/package.json +10 -9
- package/src/chat/Chat.tsx +169 -164
- package/src/chat/FileUpload/index.ts +105 -105
- package/src/chat/chat.css +403 -403
- package/src/chat/components/FileList.css +128 -128
- package/src/chat/components/FileList.tsx +73 -73
- package/src/chat/components/HistoryList.tsx +192 -192
- package/src/chat/components/JsonEditorPopup.css +80 -80
- package/src/chat/components/JsonEditorPopup.tsx +56 -56
- package/src/chat/components/JsonToMessage/JsonToMessage.css +104 -0
- package/src/chat/components/JsonToMessage/JsonToMessage.tsx +114 -0
- package/src/chat/components/JsonToMessage/JsonToMessageButton.tsx +27 -0
- package/src/chat/components/JsonToMessage/index.tsx +5 -0
- package/src/chat/components/MessageAI.tsx +24 -24
- package/src/chat/components/MessageBox.tsx +39 -0
- package/src/chat/components/MessageHuman.tsx +55 -55
- package/src/chat/components/MessageTool.tsx +46 -46
- package/src/chat/components/UsageMetadata.tsx +40 -40
- package/src/chat/context/ChatContext.tsx +32 -29
- package/src/chat/context/ExtraParamsContext.tsx +41 -41
- package/src/chat/store/index.ts +38 -24
- package/src/chat/tools.ts +33 -33
- package/src/chat/types.ts +16 -16
- package/src/graph/GraphPanel.tsx +5 -0
- package/src/graph/flattenGraph.ts +45 -0
- package/src/graph/flow.css +176 -0
- package/src/graph/index.tsx +161 -0
- package/src/hooks/useLocalStorage.ts +27 -27
- package/src/index.ts +1 -1
- package/src/login/Login.css +93 -93
- package/src/login/Login.tsx +92 -92
- package/test/App.tsx +9 -9
- package/test/main.tsx +5 -5
- package/test/vite-env.d.ts +1 -1
- package/tsconfig.json +21 -21
- package/tsconfig.node.json +9 -9
- package/vite.config.ts +22 -17
- package/dist/assets/index-BHPbGlnP.js +0 -192
- package/dist/assets/index-Du4LMUX2.css +0 -1
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from "react";
|
|
2
|
-
type ChatContextType = UnionStore<typeof globalChatStore>;
|
|
3
|
-
|
|
4
|
-
const ChatContext = createContext<ChatContextType | undefined>(undefined);
|
|
5
|
-
|
|
6
|
-
export const useChat = () => {
|
|
7
|
-
const context = useContext(ChatContext);
|
|
8
|
-
if (!context) {
|
|
9
|
-
throw new Error("useChat must be used within a ChatProvider");
|
|
10
|
-
}
|
|
11
|
-
return context;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
interface ChatProviderProps {
|
|
15
|
-
children: ReactNode;
|
|
16
|
-
}
|
|
17
|
-
import { globalChatStore } from "../store";
|
|
18
|
-
import { UnionStore, useUnionStore } from "@langgraph-js/sdk";
|
|
19
|
-
import { useStore } from "@nanostores/react";
|
|
20
|
-
export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
21
|
-
const store = useUnionStore(globalChatStore, useStore);
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
store.initClient().then((res) => {
|
|
24
|
-
store.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
1
|
+
import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from "react";
|
|
2
|
+
type ChatContextType = UnionStore<typeof globalChatStore>;
|
|
3
|
+
|
|
4
|
+
const ChatContext = createContext<ChatContextType | undefined>(undefined);
|
|
5
|
+
|
|
6
|
+
export const useChat = () => {
|
|
7
|
+
const context = useContext(ChatContext);
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new Error("useChat must be used within a ChatProvider");
|
|
10
|
+
}
|
|
11
|
+
return context;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
interface ChatProviderProps {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
import { globalChatStore } from "../store";
|
|
18
|
+
import { UnionStore, useUnionStore } from "@langgraph-js/sdk";
|
|
19
|
+
import { useStore } from "@nanostores/react";
|
|
20
|
+
export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
21
|
+
const store = useUnionStore(globalChatStore, useStore);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
store.initClient().then((res) => {
|
|
24
|
+
if (store.showHistory) {
|
|
25
|
+
store.refreshHistoryList();
|
|
26
|
+
}
|
|
27
|
+
console.log(res);
|
|
28
|
+
});
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
return <ChatContext.Provider value={store}>{children}</ChatContext.Provider>;
|
|
32
|
+
};
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import React, { createContext, useState, useEffect, useContext, ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
interface ExtraParamsContextType {
|
|
4
|
-
extraParams: object;
|
|
5
|
-
setExtraParams: (params: object) => void;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const ExtraParamsContext = createContext<ExtraParamsContextType | undefined>(undefined);
|
|
9
|
-
|
|
10
|
-
export const ExtraParamsProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|
11
|
-
const [extraParams, setExtraParamsState] = useState<object>(() => {
|
|
12
|
-
const savedParams = localStorage.getItem("extraParams");
|
|
13
|
-
try {
|
|
14
|
-
return savedParams ? JSON.parse(savedParams) : {};
|
|
15
|
-
} catch (e) {
|
|
16
|
-
console.error("Failed to parse extraParams from localStorage", e);
|
|
17
|
-
return {};
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
localStorage.setItem("extraParams", JSON.stringify(extraParams));
|
|
23
|
-
}, [extraParams]);
|
|
24
|
-
|
|
25
|
-
const setExtraParams = (params: object) => {
|
|
26
|
-
setExtraParamsState(params);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<ExtraParamsContext.Provider value={{ extraParams, setExtraParams }}>
|
|
31
|
-
{children}
|
|
32
|
-
</ExtraParamsContext.Provider>
|
|
33
|
-
);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const useExtraParams = (): ExtraParamsContextType => {
|
|
37
|
-
const context = useContext(ExtraParamsContext);
|
|
38
|
-
if (context === undefined) {
|
|
39
|
-
throw new Error('useExtraParams must be used within an ExtraParamsProvider');
|
|
40
|
-
}
|
|
41
|
-
return context;
|
|
1
|
+
import React, { createContext, useState, useEffect, useContext, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ExtraParamsContextType {
|
|
4
|
+
extraParams: object;
|
|
5
|
+
setExtraParams: (params: object) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const ExtraParamsContext = createContext<ExtraParamsContextType | undefined>(undefined);
|
|
9
|
+
|
|
10
|
+
export const ExtraParamsProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|
11
|
+
const [extraParams, setExtraParamsState] = useState<object>(() => {
|
|
12
|
+
const savedParams = localStorage.getItem("extraParams");
|
|
13
|
+
try {
|
|
14
|
+
return savedParams ? JSON.parse(savedParams) : {};
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.error("Failed to parse extraParams from localStorage", e);
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
localStorage.setItem("extraParams", JSON.stringify(extraParams));
|
|
23
|
+
}, [extraParams]);
|
|
24
|
+
|
|
25
|
+
const setExtraParams = (params: object) => {
|
|
26
|
+
setExtraParamsState(params);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<ExtraParamsContext.Provider value={{ extraParams, setExtraParams }}>
|
|
31
|
+
{children}
|
|
32
|
+
</ExtraParamsContext.Provider>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const useExtraParams = (): ExtraParamsContextType => {
|
|
37
|
+
const context = useContext(ExtraParamsContext);
|
|
38
|
+
if (context === undefined) {
|
|
39
|
+
throw new Error('useExtraParams must be used within an ExtraParamsProvider');
|
|
40
|
+
}
|
|
41
|
+
return context;
|
|
42
42
|
};
|
package/src/chat/store/index.ts
CHANGED
|
@@ -1,24 +1,38 @@
|
|
|
1
|
-
import { createChatStore } from "@langgraph-js/sdk";
|
|
2
|
-
const F =
|
|
3
|
-
localStorage.getItem("withCredentials") === "true"
|
|
4
|
-
? (url: string, options: RequestInit) => {
|
|
5
|
-
options.credentials = "include";
|
|
6
|
-
return fetch(url, options);
|
|
7
|
-
}
|
|
8
|
-
: fetch;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import { createChatStore } from "@langgraph-js/sdk";
|
|
2
|
+
const F =
|
|
3
|
+
localStorage.getItem("withCredentials") === "true"
|
|
4
|
+
? (url: string, options: RequestInit) => {
|
|
5
|
+
options.credentials = "include";
|
|
6
|
+
return fetch(url, options);
|
|
7
|
+
}
|
|
8
|
+
: fetch;
|
|
9
|
+
|
|
10
|
+
const getLocalConfig = () => {
|
|
11
|
+
return {
|
|
12
|
+
showHistory: localStorage.getItem("showHistory") === "true" || false,
|
|
13
|
+
showGraph: localStorage.getItem("showGraph") === "true" || false,
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export const setLocalConfig = (config: Partial<{ showHistory: boolean; showGraph: boolean }>) => {
|
|
17
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
18
|
+
localStorage.setItem(key, value.toString());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const globalChatStore = createChatStore(
|
|
23
|
+
localStorage.getItem("agent_name") || "",
|
|
24
|
+
{
|
|
25
|
+
apiUrl: localStorage.getItem("apiUrl") || "http://localhost:8123",
|
|
26
|
+
defaultHeaders: JSON.parse(localStorage.getItem("code") || "{}"),
|
|
27
|
+
callerOptions: {
|
|
28
|
+
// 携带 cookie 的写法
|
|
29
|
+
fetch: F,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
...getLocalConfig(),
|
|
34
|
+
onInit(client) {
|
|
35
|
+
client.tools.bindTools([]);
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
);
|
package/src/chat/tools.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { createFETool, ToolManager } from "@langgraph-js/sdk";
|
|
2
|
-
|
|
3
|
-
// 文件操作工具
|
|
4
|
-
export const fileTool = createFETool({
|
|
5
|
-
name: "file_operation",
|
|
6
|
-
description: "执行文件操作,包括读取和写入",
|
|
7
|
-
parameters: [
|
|
8
|
-
{
|
|
9
|
-
name: "filePath",
|
|
10
|
-
type: "string",
|
|
11
|
-
description: "文件的完整路径",
|
|
12
|
-
},
|
|
13
|
-
],
|
|
14
|
-
returnDirect: true,
|
|
15
|
-
callbackMessage: () => [{ type: "ai", content: "工作完成" }],
|
|
16
|
-
async handler(args) {
|
|
17
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
18
|
-
return [{ type: "text", text: "执行文件操作 " + args.filePath }];
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
export const askUserTool = createFETool({
|
|
23
|
-
name: "ask_user",
|
|
24
|
-
description: "询问用户",
|
|
25
|
-
parameters: [
|
|
26
|
-
{
|
|
27
|
-
name: "question",
|
|
28
|
-
type: "string",
|
|
29
|
-
description: "问题",
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
handler: ToolManager.waitForUIDone,
|
|
33
|
-
});
|
|
1
|
+
import { createFETool, ToolManager } from "@langgraph-js/sdk";
|
|
2
|
+
|
|
3
|
+
// 文件操作工具
|
|
4
|
+
export const fileTool = createFETool({
|
|
5
|
+
name: "file_operation",
|
|
6
|
+
description: "执行文件操作,包括读取和写入",
|
|
7
|
+
parameters: [
|
|
8
|
+
{
|
|
9
|
+
name: "filePath",
|
|
10
|
+
type: "string",
|
|
11
|
+
description: "文件的完整路径",
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
returnDirect: true,
|
|
15
|
+
callbackMessage: () => [{ type: "ai", content: "工作完成" }],
|
|
16
|
+
async handler(args) {
|
|
17
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
18
|
+
return [{ type: "text", text: "执行文件操作 " + args.filePath }];
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const askUserTool = createFETool({
|
|
23
|
+
name: "ask_user",
|
|
24
|
+
description: "询问用户",
|
|
25
|
+
parameters: [
|
|
26
|
+
{
|
|
27
|
+
name: "question",
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "问题",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
handler: ToolManager.waitForUIDone,
|
|
33
|
+
});
|
package/src/chat/types.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export interface Message {
|
|
2
|
-
content: string;
|
|
3
|
-
role: string;
|
|
4
|
-
name?: string;
|
|
5
|
-
metadata?: {
|
|
6
|
-
graph_id?: string;
|
|
7
|
-
};
|
|
8
|
-
thread_id?: string;
|
|
9
|
-
usage_metadata?: {
|
|
10
|
-
input_tokens: number;
|
|
11
|
-
output_tokens: number;
|
|
12
|
-
total_tokens: number;
|
|
13
|
-
};
|
|
14
|
-
spend_time?: number;
|
|
15
|
-
tool_input?: string;
|
|
16
|
-
}
|
|
1
|
+
export interface Message {
|
|
2
|
+
content: string;
|
|
3
|
+
role: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
metadata?: {
|
|
6
|
+
graph_id?: string;
|
|
7
|
+
};
|
|
8
|
+
thread_id?: string;
|
|
9
|
+
usage_metadata?: {
|
|
10
|
+
input_tokens: number;
|
|
11
|
+
output_tokens: number;
|
|
12
|
+
total_tokens: number;
|
|
13
|
+
};
|
|
14
|
+
spend_time?: number;
|
|
15
|
+
tool_input?: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AssistantGraph } from "@langgraph-js/sdk";
|
|
2
|
+
import { Node } from "@xyflow/react";
|
|
3
|
+
|
|
4
|
+
export function flattenGraph(graph: AssistantGraph["nodes"]) {
|
|
5
|
+
const flatParents = new Map<string, Node>();
|
|
6
|
+
const finalNodes: Node[] = [];
|
|
7
|
+
const createParentNode = (node: Node) => {
|
|
8
|
+
const parts = node.id.split(":");
|
|
9
|
+
const parentId = parts.slice(0, -1).join(":");
|
|
10
|
+
if (parts.length === 1) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (!flatParents.has(parentId)) {
|
|
14
|
+
const parentNode: Node = {
|
|
15
|
+
id: parentId,
|
|
16
|
+
type: "group",
|
|
17
|
+
data: {
|
|
18
|
+
label: parentId.split(":").pop(),
|
|
19
|
+
name: parentId.split(":").pop(),
|
|
20
|
+
},
|
|
21
|
+
position: { x: 0, y: 0 },
|
|
22
|
+
style: {},
|
|
23
|
+
parentId: undefined,
|
|
24
|
+
};
|
|
25
|
+
const p = createParentNode(parentNode);
|
|
26
|
+
if (p) {
|
|
27
|
+
parentNode.parentId = p.id;
|
|
28
|
+
}
|
|
29
|
+
flatParents.set(parentId, parentNode);
|
|
30
|
+
}
|
|
31
|
+
return flatParents.get(parentId);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
graph.forEach((node) => {
|
|
35
|
+
const flowNode: Node = { ...node, id: node.id.toString(), position: { x: 0, y: 0 }, data: { label: (node.id as string).split(":").pop(), name: (node.id as string).split(":").pop() } };
|
|
36
|
+
const parentNode = createParentNode(flowNode);
|
|
37
|
+
if (parentNode) {
|
|
38
|
+
flowNode.parentId = parentNode.id.toString();
|
|
39
|
+
}
|
|
40
|
+
finalNodes.push(flowNode);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const data = [...Array.from(flatParents.values()), ...finalNodes];
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/* xyflow theme files - Adjusted for a cleaner look */
|
|
2
|
+
.react-flow {
|
|
3
|
+
/* Custom Variables - Refined Palette */
|
|
4
|
+
--xy-theme-selected: #77b6ff; /* 更柔和且更具现代感的蓝色 */
|
|
5
|
+
--xy-theme-hover: #d1d5db; /* 更柔和的悬停效果 */
|
|
6
|
+
--xy-theme-edge-hover: #374151; /* 深灰色,更专业 */
|
|
7
|
+
--xy-theme-color-focus: #e5e7eb; /* 更浅的焦点颜色 */
|
|
8
|
+
|
|
9
|
+
/* Built-in Variables - Subtle Adjustments */
|
|
10
|
+
--xy-node-border-default: 1px solid #cbd5e1; /* 更柔和的边框 */
|
|
11
|
+
--xy-node-boxshadow-default: 0 2px 4px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.06); /* 更柔和的阴影 */
|
|
12
|
+
--xy-node-border-radius-default: 6px; /* 略微减小圆角 */
|
|
13
|
+
--xy-handle-background-color-default: #f9fafb; /* 更柔和的连接点背景 */
|
|
14
|
+
--xy-handle-border-color-default: #9ca3af; /* 更柔和的连接点边框 */
|
|
15
|
+
--xy-edge-label-color-default: #4b5563; /* 更深的标签颜色 */
|
|
16
|
+
--xy-node-color: #777;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.react-flow.dark {
|
|
20
|
+
--xy-node-boxshadow-default: 0 2px 4px rgba(255, 255, 255, 0.06), 0 1px 3px rgba(255, 255, 255, 0.04); /* 调整暗色阴影 */
|
|
21
|
+
--xy-theme-color-focus: #4a5568; /* 更深的暗色焦点颜色 */
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Customizing Default Theming - Enhanced Visuals */
|
|
25
|
+
|
|
26
|
+
.react-flow__node {
|
|
27
|
+
box-shadow: var(--xy-node-boxshadow-default);
|
|
28
|
+
border-radius: var(--xy-node-border-radius-default);
|
|
29
|
+
background-color: var(--xy-node-background-color-default);
|
|
30
|
+
display: flex;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
align-items: center;
|
|
33
|
+
text-align: center;
|
|
34
|
+
padding: 12px; /* 略微增加内边距 */
|
|
35
|
+
font-size: 14px; /* 略微增大字体 */
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
border: var(--xy-node-border-default);
|
|
38
|
+
color: var(--xy-node-color, var(--xy-node-color-default));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.react-flow__node.selectable:focus {
|
|
42
|
+
box-shadow: 0 0 0 3px var(--xy-theme-color-focus); /* 更柔和的焦点边框 */
|
|
43
|
+
border-color: #9ca3af; /* 更柔和的焦点边框颜色 */
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.react-flow__node.selectable:focus:active {
|
|
47
|
+
box-shadow: var(--xy-node-boxshadow-default);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.react-flow__node.selectable:hover,
|
|
51
|
+
.react-flow__node.draggable:hover {
|
|
52
|
+
border-color: var(--xy-theme-hover);
|
|
53
|
+
cursor: grab; /* 增加手型光标 */
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.react-flow__node.selectable.selected {
|
|
57
|
+
border-color: var(--xy-theme-selected);
|
|
58
|
+
box-shadow: 0 0 0 2px var(--xy-theme-selected); /* 更清晰的选中效果 */
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.react-flow__node-group {
|
|
62
|
+
background-color: rgba(147, 197, 253, 0.2); /* 更柔和的 Group 背景 */
|
|
63
|
+
border-color: #77b6ff; /* 与选中颜色一致 */
|
|
64
|
+
border-style: dashed; /* 虚线边框 */
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.react-flow__edge.selectable:hover .react-flow__edge-path,
|
|
68
|
+
.react-flow__edge.selectable.selected .react-flow__edge-path {
|
|
69
|
+
stroke: var(--xy-theme-edge-hover);
|
|
70
|
+
stroke-width: 1.5px; /* 略微加粗选中的边 */
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.react-flow__handle {
|
|
74
|
+
background-color: var(--xy-handle-background-color-default);
|
|
75
|
+
border: 1px solid var(--xy-handle-border-color-default); /* 添加边框 */
|
|
76
|
+
border-radius: 3px; /* 略微圆角 */
|
|
77
|
+
width: 10px; /* 略微增大尺寸 */
|
|
78
|
+
height: 10px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.react-flow__handle.connectionindicator:hover {
|
|
82
|
+
pointer-events: all;
|
|
83
|
+
border-color: var(--xy-theme-edge-hover);
|
|
84
|
+
background-color: white;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.react-flow__handle.connectionindicator:focus,
|
|
88
|
+
.react-flow__handle.connectingfrom,
|
|
89
|
+
.react-flow__handle.connectingto {
|
|
90
|
+
border-color: var(--xy-theme-edge-hover);
|
|
91
|
+
background-color: var(--xy-theme-selected); /* 连接时高亮 */
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.react-flow__node-resizer {
|
|
95
|
+
border-radius: 2px; /* 略微圆角 */
|
|
96
|
+
border: 1px solid #cbd5e1; /* 更柔和的边框 */
|
|
97
|
+
background-color: #f9fafb; /* 与 handle 颜色一致 */
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.react-flow__resize-control.handle {
|
|
101
|
+
background-color: var(--xy-theme-selected); /* 使用选中颜色 */
|
|
102
|
+
border-color: var(--xy-theme-selected);
|
|
103
|
+
border-radius: 2px; /* 略微圆角 */
|
|
104
|
+
width: 7px; /* 略微增大尺寸 */
|
|
105
|
+
height: 7px; /* 略微增大尺寸 */
|
|
106
|
+
}
|
|
107
|
+
.react-flow__node-group {
|
|
108
|
+
border-style: dashed;
|
|
109
|
+
background-color: transparent;
|
|
110
|
+
border-color: transparent;
|
|
111
|
+
font-size: 0.5rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.react-flow .react-flow__node-group:nth-child(1) {
|
|
115
|
+
background-color: rgba(179, 229, 252, 0.3); /* 非常浅的蓝色 */
|
|
116
|
+
border-color: #4fc3f7; /* 柔和的蓝色 */
|
|
117
|
+
color: #4fc3f7;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.react-flow .react-flow__node-group:nth-child(2) {
|
|
121
|
+
background-color: rgba(200, 230, 201, 0.3); /* 非常浅的绿色 */
|
|
122
|
+
border-color: #81c784; /* 柔和的绿色 */
|
|
123
|
+
color: #81c784;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.react-flow .react-flow__node-group:nth-child(3) {
|
|
127
|
+
background-color: rgba(255, 224, 178, 0.3); /* 非常浅的橙色 */
|
|
128
|
+
border-color: #ffb74d; /* 柔和的橙色 */
|
|
129
|
+
color: #ffb74d;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.react-flow .react-flow__node-group:nth-child(4) {
|
|
133
|
+
background-color: rgba(225, 190, 231, 0.3); /* 非常浅的紫色 */
|
|
134
|
+
border-color: #ba68c8; /* 柔和的紫色 */
|
|
135
|
+
color: #ba68c8;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.react-flow .react-flow__node-group:nth-child(5) {
|
|
139
|
+
background-color: rgba(255, 204, 204, 0.3); /* 非常浅的粉色 */
|
|
140
|
+
border-color: #e57373; /* 柔和的粉色 */
|
|
141
|
+
color: #e57373;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* 更多柔和的颜色 */
|
|
145
|
+
.react-flow .react-flow__node-group:nth-child(6) {
|
|
146
|
+
background-color: rgba(255, 245, 157, 0.3); /* 非常浅的黄色 */
|
|
147
|
+
border-color: #ffee58; /* 柔和的黄色 */
|
|
148
|
+
color: #ffee58;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.react-flow .react-flow__node-group:nth-child(7) {
|
|
152
|
+
background-color: rgba(165, 214, 167, 0.3); /* 非常浅的薄荷绿 */
|
|
153
|
+
border-color: #66bb6a; /* 柔和的薄荷绿 */
|
|
154
|
+
color: #66bb6a;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.react-flow .react-flow__node-group:nth-child(8) {
|
|
158
|
+
background-color: rgba(244, 143, 177, 0.3); /* 非常浅的玫瑰色 */
|
|
159
|
+
border-color: #f06292; /* 柔和的玫瑰色 */
|
|
160
|
+
color: #f06292;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.react-flow .react-flow__node-group:nth-child(9) {
|
|
164
|
+
background-color: rgba(210, 180, 140, 0.3); /* 非常浅的卡其色 */
|
|
165
|
+
border-color: #a1887f; /* 柔和的棕色 */
|
|
166
|
+
color: #a1887f;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.react-flow .react-flow__node-group:nth-child(10) {
|
|
170
|
+
background-color: rgba(197, 176, 213, 0.3); /* 非常浅的薰衣草色 */
|
|
171
|
+
border-color: #9575cd; /* 柔和的薰衣草色 */
|
|
172
|
+
color: #9575cd;
|
|
173
|
+
}
|
|
174
|
+
.react-flow__handle.connectionindicator {
|
|
175
|
+
visibility: hidden;
|
|
176
|
+
}
|