From 05cce8f91f7330a5ba77f23c93f9b17cdf391425 Mon Sep 17 00:00:00 2001 From: codetypes Date: Mon, 29 Sep 2025 14:35:40 +0800 Subject: [PATCH] add nanoid for node --- package-lock.json | 46 ++++++++++++++++++++++++++------- package.json | 1 + src/behavior3 | 2 +- src/components/graph.ts | 37 ++++++++++++++++---------- src/components/inspector.tsx | 1 + src/components/register-node.ts | 6 ++--- src/misc/b3type.ts | 13 +++++++--- src/misc/b3util.ts | 42 ++++++++++++++++-------------- src/misc/util.ts | 20 +++++++++++--- 9 files changed, 114 insertions(+), 54 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40471ff..fef96d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "i18next": "^23.12.2", "i18next-browser-languagedetector": "^7.2.1", "i18next-http-backend": "^2.5.2", + "nanoid": "^5.1.6", "react-i18next": "^14.1.3", "react-icons": "^5.2.1", "react-markdown": "^9.0.1", @@ -8315,10 +8316,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "dev": true, + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", "funding": [ { "type": "github", @@ -8327,10 +8327,10 @@ ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/natural-compare": { @@ -8833,6 +8833,25 @@ "dev": true, "license": "MIT" }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -17731,10 +17750,9 @@ } }, "nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "dev": true + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==" }, "natural-compare": { "version": "1.4.0", @@ -17990,6 +18008,14 @@ "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" + }, + "dependencies": { + "nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true + } } }, "postcss-import": { diff --git a/package.json b/package.json index 650ee7e..3b47d98 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "i18next": "^23.12.2", "i18next-browser-languagedetector": "^7.2.1", "i18next-http-backend": "^2.5.2", + "nanoid": "^5.1.6", "react-i18next": "^14.1.3", "react-icons": "^5.2.1", "react-markdown": "^9.0.1", diff --git a/src/behavior3 b/src/behavior3 index 9283002..16334ea 160000 --- a/src/behavior3 +++ b/src/behavior3 @@ -1 +1 @@ -Subproject commit 9283002a96ab5ff5e326919c26cb953c2285fa9c +Subproject commit 16334eab4c36a2a8b2445e9fa11e44673f0d7252 diff --git a/src/components/graph.ts b/src/components/graph.ts index 22cfa1b..fe3dd3e 100644 --- a/src/components/graph.ts +++ b/src/components/graph.ts @@ -19,7 +19,7 @@ import * as b3util from "../misc/b3util"; import { message } from "../misc/hooks"; import i18n from "../misc/i18n"; import Path from "../misc/path"; -import { readTree, writeTree } from "../misc/util"; +import { nanoid, readTree, writeTree } from "../misc/util"; import { TreeNodeState, TreeNodeStyle } from "./register-node"; type G6NodeState = Exclude["state"]; @@ -89,8 +89,8 @@ export class Graph { const dataB = nodeB.data as unknown as NodeData; return Number(dataA.id) - Number(dataB.id); }, - getHeight: ({ data }: { data: NodeData }) => data.size![1], - getWidth: ({ data }: { data: NodeData }) => data.size![0], + getHeight: ({ data }: { data: NodeData }) => data.$size![1], + getWidth: ({ data }: { data: NodeData }) => data.$size![0], getVGap: () => 10, getHGap: () => 30, }, @@ -147,7 +147,7 @@ export class Graph { private async _update(data: TreeData, refreshId: boolean = true, refreshVars: boolean = false) { this.editor.data = data; if (refreshId) { - b3util.refreshNodeData(this.data.root, 1); + b3util.refreshNodeData(this.data, this.data.root, 1); } if (refreshVars) { @@ -471,18 +471,24 @@ export class Graph { async updateNode(editNode: EditNode) { const node = this._graph.getNodeData(editNode.data.id); - const data = node.data as unknown as NodeData; + let data = node.data as unknown as NodeData; if (b3util.isNodeEqual(data, editNode.data)) { return; } + const subtree = data.path; + // update node - node.data = { ...editNode.data, size: b3util.calcSize(editNode.data) }; + data = { ...editNode.data, $size: b3util.calcSize(editNode.data) }; + if (editNode.data.name !== data.name) { + data.$id = nanoid(); + } + node.data = data as unknown as Record; this._graph.updateNodeData([node]); await this._graph.draw(); // update subtree - if (data.path !== editNode.data.path) { + if (subtree !== editNode.data.path) { this.editor.data.root = this._nodeToData("1"); await this.refresh(); } @@ -640,7 +646,7 @@ export class Graph { const id = this._dropId; const pos = this._graph.getElementPosition(id); const data = this._graph.getNodeData(id).data as unknown as NodeData; - const [w, h] = data.size!; + const [w, h] = data.$size!; const x = e.canvas.x - pos[0]; const y = e.canvas.y - pos[1]; const states = this._getState(id); @@ -803,17 +809,20 @@ export class Graph { } console.debug("parse node:", str); + const node = JSON.parse(str) as NodeData; + b3util.dfs(node, (v) => (v.$id = nanoid())); + const root = this._nodeToData("1"); let dstData: NodeData | undefined; - b3util.dfs(root, (node) => { - if (node.id === this._selectedId) { - dstData = node; + b3util.dfs(root, (v) => { + if (v.id === this._selectedId) { + dstData = v; } }); assert(dstData, this._selectedId); dstData.children ||= []; - dstData.children.push(JSON.parse(str) as NodeData); + dstData.children.push(node); this.selectNode(null); await this._update({ ...this.data, root }); this._storeHistory(); @@ -881,7 +890,7 @@ export class Graph { assert(dstData, this._selectedId); dstData.children ||= []; - dstData.children.push({ id: "", name: "unknow" }); + dstData.children.push({ id: "", name: "unknow", $id: nanoid() }); await this._update({ ...this.data, root }); this._storeHistory(); } @@ -920,7 +929,7 @@ export class Graph { hasSubtreeUpdated() { let updated = false; b3util.dfs(this.data.root, (node) => { - if (node.path && b3util.files[node.path] !== node.mtime) { + if (node.path && b3util.files[node.path] !== node.$mtime) { updated = true; } }); diff --git a/src/components/inspector.tsx b/src/components/inspector.tsx index 965b506..75ce55e 100644 --- a/src/components/inspector.tsx +++ b/src/components/inspector.tsx @@ -822,6 +822,7 @@ const NodeInspector: FC = () => { if (editingNode.data.name !== newname) { workspace.onEditingNode({ data: { + $id: editingNode.data.$id, id: editingNode.data.id, name: workspace.nodeDefs.get(newname)?.name ?? newname, desc: editingNode.data.desc, diff --git a/src/components/register-node.ts b/src/components/register-node.ts index 2a0dd0b..246cc7a 100644 --- a/src/components/register-node.ts +++ b/src/components/register-node.ts @@ -388,7 +388,7 @@ class TreeNode extends Rect { } private drawStatusIcon(attributes: Required, container: Group) { - const status = ((this._data.status ?? 0) & 0b111).toString(2).padStart(3, "0"); + const status = ((this._data.$status ?? 0) & 0b111).toString(2).padStart(3, "0"); this.upsert( "status", GImage, @@ -758,8 +758,8 @@ class TreeNode extends Rect { color = NODE_COLORS[classify]; } - assert(data.size); - const [width, height] = data.size; + assert(data.$size); + const [width, height] = data.$size; this._prefix = (node.prefix as string) ?? ""; this._width = width; diff --git a/src/misc/b3type.ts b/src/misc/b3type.ts index 4753392..d955289 100644 --- a/src/misc/b3type.ts +++ b/src/misc/b3type.ts @@ -27,10 +27,13 @@ export interface NodeData { disabled?: boolean; path?: string; + // nanoid, for override + $id: string; + // for runtime - mtime?: number; - size?: number[]; - status?: number; + $mtime?: number; + $size?: number[]; + $status?: number; } export type NodeLayout = "compact" | "normal"; @@ -71,6 +74,10 @@ export interface TreeData { import: string[]; vars: VarDecl[]; root: NodeData; + + $override: { + [key: string]: Pick; + }; } export const getNodeType = (def: NodeDef): NodeType => { diff --git a/src/misc/b3util.ts b/src/misc/b3util.ts index 4f2d545..df69105 100644 --- a/src/misc/b3util.ts +++ b/src/misc/b3util.ts @@ -20,7 +20,7 @@ import { VERSION, } from "./b3type"; import Path from "./path"; -import { readJson, readTree, readWorkspace } from "./util"; +import { nanoid, readJson, readTree, readWorkspace } from "./util"; export class NodeDefs extends Map { get(key: string): NodeDef { @@ -520,6 +520,7 @@ const parsingStack: string[] = []; export const createNode = (data: NodeData, includeChildren: boolean = true) => { const node: NodeData = { + $id: data.$id, id: data.id, name: data.name, desc: data.desc, @@ -598,7 +599,7 @@ const appendStatusFlag = (status: number, childStatus: number) => { }; const buildStatusFlag = (data: NodeData, childStatus: number) => { - let status = data.status!; + let status = data.$status!; const def = nodeDefs.get(data.name); if (def.status?.length) { const childSuccess = (childStatus >> StatusFlag.SUCCESS) & 1; @@ -639,9 +640,9 @@ const buildStatusFlag = (data: NodeData, childStatus: number) => { break; } }); - data.status = status; + data.$status = status; } else { - data.status = status | childStatus; + data.$status = status | childStatus; } }; @@ -694,9 +695,9 @@ export const checkTreeData = (data: NodeData) => { return true; }; -export const refreshNodeData = (node: NodeData, id: number) => { +export const refreshNodeData = (tree: TreeData, node: NodeData, id: number) => { node.id = (id++).toString(); - node.size = calcSize(node); + node.$size = calcSize(node); const def = nodeDefs.get(node.name); @@ -718,16 +719,16 @@ export const refreshNodeData = (node: NodeData, id: number) => { parsingStack.push(node.path); try { const subtreePath = workdir + "/" + node.path; - const subtree = readTree(subtreePath).root; - id = refreshNodeData(subtree, --id); + const subtree = readTree(subtreePath); + id = refreshNodeData(subtree, subtree.root, --id); node.name = subtree.name; node.desc = subtree.desc; - node.args = subtree.args; - node.input = subtree.input; - node.output = subtree.output; - node.children = subtree.children; - node.mtime = fs.statSync(subtreePath).mtimeMs; - node.size = calcSize(node); + node.args = subtree.root.args; + node.input = subtree.root.input; + node.output = subtree.root.output; + node.children = subtree.root.children; + node.$mtime = fs.statSync(subtreePath).mtimeMs; + node.$size = calcSize(node); } catch (e) { alertError(`解析子树失败:${node.path}`); console.log("parse subtree:", e); @@ -735,16 +736,16 @@ export const refreshNodeData = (node: NodeData, id: number) => { parsingStack.pop(); } else if (node.children?.length) { for (let i = 0; i < node.children.length; i++) { - id = refreshNodeData(node.children[i], id); + id = refreshNodeData(tree, node.children[i], id); } } - node.status = toStatusFlag(node); + node.$status = toStatusFlag(node); if (node.children) { let childStatus = 0; node.children.forEach((child) => { - if (child.status && !child.disabled) { - childStatus = appendStatusFlag(childStatus, child.status); + if (child.$status && !child.disabled) { + childStatus = appendStatusFlag(childStatus, child.$status); } }); buildStatusFlag(node, childStatus); @@ -756,7 +757,7 @@ export const refreshNodeData = (node: NodeData, id: number) => { export const createBuildData = (path: string) => { try { const treeModel: TreeData = readTree(path); - refreshNodeData(treeModel.root, 1); + refreshNodeData(treeModel, treeModel.root, 1); dfs(treeModel.root, (node) => (node.id = treeModel.prefix + node.id)); treeModel.name = Path.basenameWithoutExt(path); treeModel.root = createFileData(treeModel.root, true); @@ -885,6 +886,7 @@ export const loadModule = async (path: string) => { export const createFileData = (data: NodeData, includeSubtree?: boolean) => { const nodeData: NodeData = { + $id: data.$id, id: data.id, name: data.name, desc: data.desc || undefined, @@ -926,7 +928,9 @@ export const createNewTree = (path: string) => { root: { id: "1", name: "Sequence", + $id: nanoid(), }, + $override: {}, }; return tree; }; diff --git a/src/misc/util.ts b/src/misc/util.ts index ea49060..e6e1fec 100644 --- a/src/misc/util.ts +++ b/src/misc/util.ts @@ -1,9 +1,15 @@ import * as fs from "fs"; +import { customAlphabet } from "nanoid"; import { type WorkspaceModel } from "../contexts/workspace-context"; -import { VarDecl, VERSION, type TreeData } from "./b3type"; +import { VERSION, type TreeData } from "./b3type"; import { dfs } from "./b3util"; import Path from "./path"; +export const nanoid = customAlphabet( + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + 10 +); + export const readJson = (path: string): T => { const str = fs.readFileSync(path, "utf-8"); return JSON.parse(str); @@ -21,16 +27,21 @@ export const readWorkspace = (path: string) => { }; export const readTree = (path: string) => { - const data = readJson(path) as TreeData & { declvar?: VarDecl[] }; + const data = readJson(path) as TreeData; data.version = data.version ?? VERSION; data.prefix = data.prefix ?? ""; data.group = data.group || []; data.import = data.import || []; - data.vars = data.vars || data.declvar || []; + data.vars = data.vars || []; data.root = data.root || {}; // compatible with old version - dfs(data.root, (node) => (node.id = node.id.toString())); + dfs(data.root, (node) => { + node.id = node.id.toString(); + if (!node.$id) { + node.$id = nanoid(); + } + }); return data; }; @@ -46,6 +57,7 @@ export const writeTree = (path: string, data: TreeData) => { import: data.import, vars: data.vars, root: data.root, + $override: data.$override, }); };