From 7cee076245cd4cbdf7bf11b5e8bbbcc44b95738f Mon Sep 17 00:00:00 2001 From: codetypes Date: Fri, 7 Feb 2025 14:32:48 +0800 Subject: [PATCH] refactor command line base library --- src/components/editor.tsx | 60 +++++++++++--- src/components/register-node.ts | 87 ++++++++++++++++++++- src/misc/b3util.ts | 134 ++++---------------------------- 3 files changed, 151 insertions(+), 130 deletions(-) diff --git a/src/components/editor.tsx b/src/components/editor.tsx index a57619c..89dc700 100644 --- a/src/components/editor.tsx +++ b/src/components/editor.tsx @@ -28,6 +28,7 @@ import { VscCaseSensitive } from "react-icons/vsc"; import { mergeRefs } from "react-merge-refs"; import { useDebounceCallback } from "usehooks-ts"; import "./register-node"; +import { calcTreeDataSize } from "./register-node"; export interface EditorProps extends React.HTMLAttributes { data: EditorStore; @@ -45,6 +46,40 @@ interface FilterOption { isFocused: boolean; } +const createTreeData = (node: NodeModel, parent?: string) => { + return b3util.createTreeData(node, parent, calcTreeDataSize); +}; + +const refreshTreeData = (data: TreeGraphData) => { + data.size = calcTreeDataSize(data); + if (data.children) { + for (const child of data.children) { + refreshTreeData(child); + } + } +}; + +const isSubtreeUpdated = (data: TreeGraphData) => { + if (data.path) { + try { + const subtreePath = useWorkspace.getState().workdir + "/" + data.path; + if (fs.statSync(subtreePath).mtimeMs !== data.lastModified) { + return true; + } + } catch (error) { + return true; + } + } + if (data.children) { + for (const child of data.children) { + if (isSubtreeUpdated(child)) { + return true; + } + } + } + return false; +}; + const createMenu = () => { const t = i18n.t; const MenuItem: FC = (itemProps) => { @@ -331,7 +366,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . const rootData = findDataById("1"); const rootNode = b3util.createNode(rootData); editor.modifiedTime = fs.statSync(editor.path).mtimeMs; - editor.data = b3util.createTreeData(rootNode); + editor.data = createTreeData(rootNode); editor.autoId = b3util.refreshTreeDataId(editor.data); editor.graph.changeData(editor.data); editor.graph.layout(); @@ -340,7 +375,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . const reload = () => { const file = readJson(editor.path) as TreeModel; - editor.data = b3util.createTreeData(file.root); + editor.data = createTreeData(file.root); editor.autoId = b3util.refreshTreeDataId(editor.data); editor.unsave = false; updateState(); @@ -354,7 +389,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . const checkSubtree = () => { if (editor.graph) { const data = findDataById("1"); - if (b3util.isSubtreeUpdated(data)) { + if (isSubtreeUpdated(data)) { refresh(); pushHistory(); onChange(); @@ -395,7 +430,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . return; } b3util.copyFromNode(data, newNode); - data.size = b3util.calcTreeDataSize(data); + data.size = calcTreeDataSize(data); if (oldNode.path !== newNode.path) { refresh(); selectNode(null); @@ -475,7 +510,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . return; } const curNodeData = findDataById(editor.selectedId); - const data = b3util.createTreeData(JSON.parse(str), editor.selectedId); + const data = createTreeData(JSON.parse(str), editor.selectedId); editor.autoId = b3util.refreshTreeDataId(data, editor.autoId); selectNode(null); curNodeData.children ||= []; @@ -510,13 +545,13 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . if (curNodeData.parent) { const parentData = findDataById(curNodeData.parent); const idx = parentData.children!.indexOf(curNodeData); - const data = b3util.createTreeData(JSON.parse(str), editor.selectedId); + const data = createTreeData(JSON.parse(str), editor.selectedId); editor.autoId = b3util.refreshTreeDataId(data, editor.autoId); parentData.children![idx] = data; refreshItem(parentData); updateGrahp(); } else { - editor.data = b3util.createTreeData(JSON.parse(str), editor.selectedId); + editor.data = createTreeData(JSON.parse(str), editor.selectedId); editor.autoId = b3util.refreshTreeDataId(editor.data); editor.graph.changeData(editor.data); editor.graph.render(); @@ -552,7 +587,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . name: "unknow", }; curNodeData.children ||= []; - curNodeData.children.push(b3util.createTreeData(newNodeData, editor.selectedId)); + curNodeData.children.push(createTreeData(newNodeData, editor.selectedId)); refreshItem(curNodeData); updateGrahp(); pushHistory(); @@ -590,7 +625,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . }; const useStackData = (data: NodeModel) => { - editor.data = b3util.createTreeData(data); + editor.data = createTreeData(data); editor.autoId = b3util.refreshTreeDataId(editor.data); editor.graph.changeData(editor.data); editor.graph.layout(); @@ -641,7 +676,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . fs.writeFileSync(path, JSON.stringify(treeModel, null, 2)); workspace.updateFileMeta(editor); editor.unsave = false; - editor.data = b3util.createTreeData(root); + editor.data = createTreeData(root); editor.autoId = b3util.refreshTreeDataId(editor.data); editor.graph.changeData(editor.data); editor.graph.layout(); @@ -877,7 +912,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . const dstData = findDataById(dstNode.getID()); if (exploreNode) { - const newTreeData: TreeGraphData = b3util.createTreeData( + const newTreeData: TreeGraphData = createTreeData( { id: editor.autoId++, name: exploreNode, @@ -889,7 +924,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . refreshItem(dstData); srcNodeId = newTreeData.id; } else if (exploreFile && exploreFile !== editor.path) { - const newTreeData: TreeGraphData = b3util.createTreeData( + const newTreeData: TreeGraphData = createTreeData( { id: editor.autoId++, name: "unknow", @@ -968,6 +1003,7 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . onChange(); }); + refreshTreeData(editor.data); editor.graph.data(editor.data); editor.graph.render(); if (editor.graphMatrix) { diff --git a/src/components/register-node.ts b/src/components/register-node.ts index 5cbb866..0d786f5 100644 --- a/src/components/register-node.ts +++ b/src/components/register-node.ts @@ -1,9 +1,94 @@ import { useWorkspace } from "@/contexts/workspace-context"; import { TreeGraphData, getNodeType } from "@/misc/b3type"; -import { checkTreeData, cutWordTo, toBreakWord } from "@/misc/b3util"; +import { checkTreeData } from "@/misc/b3util"; + import i18n from "@/misc/i18n"; +import { isMacos } from "@/misc/keys"; import G6 from "@antv/g6"; +let ctx: CanvasRenderingContext2D | null = null; +let defaultFontSize = ""; +const textWidthMap = new Map(); + +const calcTextWith = (text: string, fontSize?: string) => { + fontSize = fontSize ?? defaultFontSize; + let b3Workspace: HTMLDivElement | null; + let css: CSSStyleDeclaration | null; + if (!fontSize) { + b3Workspace = document.querySelector(".b3-workspace"); + if (b3Workspace) { + css = getComputedStyle(b3Workspace); + defaultFontSize = css.fontSize || "13px"; + fontSize = defaultFontSize; + } + } + const key = `${text}-${fontSize}`; + let width = textWidthMap.get(key); + if (!width) { + b3Workspace ||= document.querySelector(".b3-workspace"); + if (b3Workspace) { + css ||= getComputedStyle(b3Workspace); + ctx = ctx || document.createElement("canvas").getContext("2d")!; + ctx.font = `${fontSize} ${css.fontFamily}`; + const metrics = ctx.measureText(text); + width = metrics.width; + width = width - (isMacos ? 1.6 : 0.8); + textWidthMap.set(key, width); + } + } + return width ?? 13; +}; + +const cutWordTo = (str: string, maxWidth: number, fontSize?: string) => { + let i = 0; + for (; i < str.length; i++) { + maxWidth -= calcTextWith(str.charAt(i), fontSize); + if (maxWidth < 0) { + i--; + break; + } + } + return str.slice(0, i) + (i < str.length - 1 ? "..." : ""); +}; + +const toBreakWord = (str: string, maxWidth: number, fontSize?: string) => { + const chars: string[] = []; + let line = str.length > 0 ? 1 : 0; + let width = maxWidth; + for (let i = 0; i < str.length; i++) { + width -= calcTextWith(str.charAt(i), fontSize); + if (width > 0) { + chars.push(str.charAt(i)); + } else { + width = maxWidth; + line++; + chars.push("\n"); + i--; + } + } + return { + str: chars.join(""), + line, + }; +}; + +export const calcTreeDataSize = (data: TreeGraphData) => { + let height = 50 + 2; + const updateHeight = (obj: any) => { + if ((Array.isArray(obj) && obj.length) || (obj && Object.keys(obj).length > 0)) { + const { line } = toBreakWord(`${i18n.t("regnode.args")}${JSON.stringify(obj)}`, 200); + height += 20 * line; + } + }; + if (data.path) { + height += 20; + } + updateHeight(data.args); + updateHeight(data.input); + updateHeight(data.output); + return [220, height]; +}; + const NODE_COLORS: any = { ["Composite"]: "#34d800", ["Decorator"]: "#ff6700", diff --git a/src/misc/b3util.ts b/src/misc/b3util.ts index 0c647db..e31842f 100644 --- a/src/misc/b3util.ts +++ b/src/misc/b3util.ts @@ -14,75 +14,15 @@ import { TreeModel, unknownNodeDef, } from "@/misc/b3type"; -import i18n from "@/misc/i18n"; import * as fs from "fs"; import { message } from "./hooks"; -import { isMacos } from "./keys"; import Path from "./path"; import { zhNodeDef } from "./template"; -let ctx: CanvasRenderingContext2D | null = null; -let defaultFontSize = ""; -const textWidthMap = new Map(); - -const isAsciiChar = (c: number) => { - return (c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f); -}; - -const calcTextWith = (text: string, fontSize?: string) => { - fontSize = fontSize ?? defaultFontSize; - let b3Workspace: HTMLDivElement | null; - let css: CSSStyleDeclaration | null; - if (!fontSize) { - b3Workspace = document.querySelector(".b3-workspace"); - if (b3Workspace) { - css = getComputedStyle(b3Workspace); - defaultFontSize = css.fontSize || "13px"; - fontSize = defaultFontSize; - } - } - const key = `${text}-${fontSize}`; - let width = textWidthMap.get(key); - if (!width) { - b3Workspace ||= document.querySelector(".b3-workspace"); - if (b3Workspace) { - css ||= getComputedStyle(b3Workspace); - ctx = ctx || document.createElement("canvas").getContext("2d")!; - ctx.font = `${fontSize} ${css.fontFamily}`; - const metrics = ctx.measureText(text); - width = metrics.width; - width = width - (isMacos ? 1.6 : 0.8); - textWidthMap.set(key, width); - } - } - return width ?? 13; -}; - export const isSubtreeRoot = (data: TreeGraphData) => { return data.path && data.id.toString() !== "1"; }; -export const isSubtreeUpdated = (data: TreeGraphData) => { - if (data.path) { - try { - const subtreePath = useWorkspace.getState().workdir + "/" + data.path; - if (fs.statSync(subtreePath).mtimeMs !== data.lastModified) { - return true; - } - } catch (error) { - return true; - } - } - if (data.children) { - for (const child of data.children) { - if (isSubtreeUpdated(child)) { - return true; - } - } - } - return false; -}; - export const isNodeEqual = (node1: NodeModel, node2: NodeModel) => { if ( node1.name === node2.name && @@ -515,23 +455,6 @@ export const refreshTreeDataId = (data: TreeGraphData, id?: number) => { return id; }; -export const calcTreeDataSize = (data: TreeGraphData) => { - let height = 50 + 2; - const updateHeight = (obj: any) => { - if ((Array.isArray(obj) && obj.length) || (obj && Object.keys(obj).length > 0)) { - const { line } = toBreakWord(`${i18n.t("regnode.args")}${JSON.stringify(obj)}`, 200); - height += 20 * line; - } - }; - if (data.path) { - height += 20; - } - updateHeight(data.args); - updateHeight(data.input); - updateHeight(data.output); - return [220, height]; -}; - export const checkChildrenLimit = (data: TreeGraphData) => { const conf = data.def; if (conf.children !== undefined && conf.children !== -1) { @@ -582,7 +505,11 @@ export const checkTreeData = (data: TreeGraphData) => { return true; }; -export const createTreeData = (node: NodeModel, parent?: string) => { +export const createTreeData = ( + node: NodeModel, + parent?: string, + calcSize?: (d: TreeGraphData) => number[] +) => { const workspace = useWorkspace.getState(); let treeData: TreeGraphData = { id: node.id.toFixed(), @@ -604,7 +531,9 @@ export const createTreeData = (node: NodeModel, parent?: string) => { } }); - treeData.size = calcTreeDataSize(treeData); + if (calcSize) { + treeData.size = calcSize(treeData); + } if (!parent) { parsingStack.length = 0; @@ -613,12 +542,14 @@ export const createTreeData = (node: NodeModel, parent?: string) => { if (node.children) { treeData.children = []; node.children.forEach((child) => { - treeData.children!.push(createTreeData(child, treeData.id)); + treeData.children!.push(createTreeData(child, treeData.id, calcSize)); }); } else if (node.path) { if (parsingStack.indexOf(node.path) >= 0) { treeData.path = node.path; - treeData.size = calcTreeDataSize(treeData); + if (calcSize) { + treeData.size = calcSize(treeData); + } message.error(`循环引用节点:${node.path}`, 4); return treeData; } @@ -626,21 +557,23 @@ export const createTreeData = (node: NodeModel, parent?: string) => { try { const subtreePath = workspace.workdir + "/" + node.path; const str = fs.readFileSync(subtreePath, "utf8"); - treeData = createTreeData(JSON.parse(str).root, treeData.id); + treeData = createTreeData(JSON.parse(str).root, treeData.id, calcSize); treeData.lastModified = fs.statSync(subtreePath).mtimeMs; treeData.path = node.path; treeData.debug = node.debug; treeData.disabled = node.disabled; treeData.parent = parent; treeData.id = node.id.toFixed(); - treeData.size = calcTreeDataSize(treeData); + if (calcSize) { + treeData.size = calcSize(treeData); + } } catch (e) { message.error(`解析子树失败:${node.path}`); console.log("parse subtree:", e); } parsingStack.pop(); } - calcTreeDataSize(treeData); + return treeData; }; @@ -705,39 +638,6 @@ export const isTreeFile = (path: string) => { return path.toLocaleLowerCase().endsWith(".json"); }; -export const toBreakWord = (str: string, maxWidth: number, fontSize?: string) => { - const chars: string[] = []; - let line = str.length > 0 ? 1 : 0; - let width = maxWidth; - for (let i = 0; i < str.length; i++) { - width -= calcTextWith(str.charAt(i), fontSize); - if (width > 0) { - chars.push(str.charAt(i)); - } else { - width = maxWidth; - line++; - chars.push("\n"); - i--; - } - } - return { - str: chars.join(""), - line, - }; -}; - -export const cutWordTo = (str: string, maxWidth: number, fontSize?: string) => { - let i = 0; - for (; i < str.length; i++) { - maxWidth -= calcTextWith(str.charAt(i), fontSize); - if (maxWidth < 0) { - i--; - break; - } - } - return str.slice(0, i) + (i < str.length - 1 ? "..." : ""); -}; - export const createProject = (path: string) => { fs.writeFileSync(Path.dirname(path) + "/node-config.b3-setting", zhNodeDef()); fs.writeFileSync(