add nanoid for node

This commit is contained in:
codetypes committed 2025-09-29 14:44:03 +08:00
1 parent bbdf7a95d6
commit 05cce8f91f
9 files changed
+114 -54

No files matched your search

+36 -10
View File
@@ -22,6 +22,7 @@
"i18next": "^23.12.2", "i18next": "^23.12.2",
"i18next-browser-languagedetector": "^7.2.1", "i18next-browser-languagedetector": "^7.2.1",
"i18next-http-backend": "^2.5.2", "i18next-http-backend": "^2.5.2",
"nanoid": "^5.1.6",
"react-i18next": "^14.1.3", "react-i18next": "^14.1.3",
"react-icons": "^5.2.1", "react-icons": "^5.2.1",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
@@ -8315,10 +8316,9 @@
} }
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.8", "version": "5.1.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -8327,10 +8327,10 @@
], ],
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.js"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^18 || >=20"
} }
}, },
"node_modules/natural-compare": { "node_modules/natural-compare": {
@@ -8833,6 +8833,25 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -17731,10 +17750,9 @@
} }
}, },
"nanoid": { "nanoid": {
"version": "3.3.8", "version": "5.1.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="
"dev": true
}, },
"natural-compare": { "natural-compare": {
"version": "1.4.0", "version": "1.4.0",
@@ -17990,6 +18008,14 @@
"nanoid": "^3.3.8", "nanoid": "^3.3.8",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"source-map-js": "^1.2.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": { "postcss-import": {
+1
View File
@@ -38,6 +38,7 @@
"i18next": "^23.12.2", "i18next": "^23.12.2",
"i18next-browser-languagedetector": "^7.2.1", "i18next-browser-languagedetector": "^7.2.1",
"i18next-http-backend": "^2.5.2", "i18next-http-backend": "^2.5.2",
"nanoid": "^5.1.6",
"react-i18next": "^14.1.3", "react-i18next": "^14.1.3",
"react-icons": "^5.2.1", "react-icons": "^5.2.1",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
+23 -14
View File
@@ -19,7 +19,7 @@ import * as b3util from "../misc/b3util";
import { message } from "../misc/hooks"; import { message } from "../misc/hooks";
import i18n from "../misc/i18n"; import i18n from "../misc/i18n";
import Path from "../misc/path"; import Path from "../misc/path";
import { readTree, writeTree } from "../misc/util"; import { nanoid, readTree, writeTree } from "../misc/util";
import { TreeNodeState, TreeNodeStyle } from "./register-node"; import { TreeNodeState, TreeNodeStyle } from "./register-node";
type G6NodeState = Exclude<G6GraphOptions["node"], undefined>["state"]; type G6NodeState = Exclude<G6GraphOptions["node"], undefined>["state"];
@@ -89,8 +89,8 @@ export class Graph {
const dataB = nodeB.data as unknown as NodeData; const dataB = nodeB.data as unknown as NodeData;
return Number(dataA.id) - Number(dataB.id); return Number(dataA.id) - Number(dataB.id);
}, },
getHeight: ({ data }: { data: NodeData }) => data.size![1], getHeight: ({ data }: { data: NodeData }) => data.$size![1],
getWidth: ({ data }: { data: NodeData }) => data.size![0], getWidth: ({ data }: { data: NodeData }) => data.$size![0],
getVGap: () => 10, getVGap: () => 10,
getHGap: () => 30, getHGap: () => 30,
}, },
@@ -147,7 +147,7 @@ export class Graph {
private async _update(data: TreeData, refreshId: boolean = true, refreshVars: boolean = false) { private async _update(data: TreeData, refreshId: boolean = true, refreshVars: boolean = false) {
this.editor.data = data; this.editor.data = data;
if (refreshId) { if (refreshId) {
b3util.refreshNodeData(this.data.root, 1); b3util.refreshNodeData(this.data, this.data.root, 1);
} }
if (refreshVars) { if (refreshVars) {
@@ -471,18 +471,24 @@ export class Graph {
async updateNode(editNode: EditNode) { async updateNode(editNode: EditNode) {
const node = this._graph.getNodeData(editNode.data.id); 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)) { if (b3util.isNodeEqual(data, editNode.data)) {
return; return;
} }
const subtree = data.path;
// update node // 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<string, unknown>;
this._graph.updateNodeData([node]); this._graph.updateNodeData([node]);
await this._graph.draw(); await this._graph.draw();
// update subtree // update subtree
if (data.path !== editNode.data.path) { if (subtree !== editNode.data.path) {
this.editor.data.root = this._nodeToData("1"); this.editor.data.root = this._nodeToData("1");
await this.refresh(); await this.refresh();
} }
@@ -640,7 +646,7 @@ export class Graph {
const id = this._dropId; const id = this._dropId;
const pos = this._graph.getElementPosition(id); const pos = this._graph.getElementPosition(id);
const data = this._graph.getNodeData(id).data as unknown as NodeData; 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 x = e.canvas.x - pos[0];
const y = e.canvas.y - pos[1]; const y = e.canvas.y - pos[1];
const states = this._getState(id); const states = this._getState(id);
@@ -803,17 +809,20 @@ export class Graph {
} }
console.debug("parse node:", str); console.debug("parse node:", str);
const node = JSON.parse(str) as NodeData;
b3util.dfs(node, (v) => (v.$id = nanoid()));
const root = this._nodeToData("1"); const root = this._nodeToData("1");
let dstData: NodeData | undefined; let dstData: NodeData | undefined;
b3util.dfs(root, (node) => { b3util.dfs(root, (v) => {
if (node.id === this._selectedId) { if (v.id === this._selectedId) {
dstData = node; dstData = v;
} }
}); });
assert(dstData, this._selectedId); assert(dstData, this._selectedId);
dstData.children ||= []; dstData.children ||= [];
dstData.children.push(JSON.parse(str) as NodeData); dstData.children.push(node);
this.selectNode(null); this.selectNode(null);
await this._update({ ...this.data, root }); await this._update({ ...this.data, root });
this._storeHistory(); this._storeHistory();
@@ -881,7 +890,7 @@ export class Graph {
assert(dstData, this._selectedId); assert(dstData, this._selectedId);
dstData.children ||= []; dstData.children ||= [];
dstData.children.push({ id: "", name: "unknow" }); dstData.children.push({ id: "", name: "unknow", $id: nanoid() });
await this._update({ ...this.data, root }); await this._update({ ...this.data, root });
this._storeHistory(); this._storeHistory();
} }
@@ -920,7 +929,7 @@ export class Graph {
hasSubtreeUpdated() { hasSubtreeUpdated() {
let updated = false; let updated = false;
b3util.dfs(this.data.root, (node) => { 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; updated = true;
} }
}); });
+1
View File
@@ -822,6 +822,7 @@ const NodeInspector: FC = () => {
if (editingNode.data.name !== newname) { if (editingNode.data.name !== newname) {
workspace.onEditingNode({ workspace.onEditingNode({
data: { data: {
$id: editingNode.data.$id,
id: editingNode.data.id, id: editingNode.data.id,
name: workspace.nodeDefs.get(newname)?.name ?? newname, name: workspace.nodeDefs.get(newname)?.name ?? newname,
desc: editingNode.data.desc, desc: editingNode.data.desc,
+3 -3
View File
@@ -388,7 +388,7 @@ class TreeNode extends Rect {
} }
private drawStatusIcon(attributes: Required<RectStyleProps>, container: Group) { private drawStatusIcon(attributes: Required<RectStyleProps>, 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( this.upsert(
"status", "status",
GImage, GImage,
@@ -758,8 +758,8 @@ class TreeNode extends Rect {
color = NODE_COLORS[classify]; color = NODE_COLORS[classify];
} }
assert(data.size); assert(data.$size);
const [width, height] = data.size; const [width, height] = data.$size;
this._prefix = (node.prefix as string) ?? ""; this._prefix = (node.prefix as string) ?? "";
this._width = width; this._width = width;
+10 -3
View File
@@ -27,10 +27,13 @@ export interface NodeData {
disabled?: boolean; disabled?: boolean;
path?: string; path?: string;
// nanoid, for override
$id: string;
// for runtime // for runtime
mtime?: number; $mtime?: number;
size?: number[]; $size?: number[];
status?: number; $status?: number;
} }
export type NodeLayout = "compact" | "normal"; export type NodeLayout = "compact" | "normal";
@@ -71,6 +74,10 @@ export interface TreeData {
import: string[]; import: string[];
vars: VarDecl[]; vars: VarDecl[];
root: NodeData; root: NodeData;
$override: {
[key: string]: Pick<NodeData, "desc" | "input" | "output" | "args" | "debug" | "disabled">;
};
} }
export const getNodeType = (def: NodeDef): NodeType => { export const getNodeType = (def: NodeDef): NodeType => {
+23 -19
View File
@@ -20,7 +20,7 @@ import {
VERSION, VERSION,
} from "./b3type"; } from "./b3type";
import Path from "./path"; import Path from "./path";
import { readJson, readTree, readWorkspace } from "./util"; import { nanoid, readJson, readTree, readWorkspace } from "./util";
export class NodeDefs extends Map<string, NodeDef> { export class NodeDefs extends Map<string, NodeDef> {
get(key: string): NodeDef { get(key: string): NodeDef {
@@ -520,6 +520,7 @@ const parsingStack: string[] = [];
export const createNode = (data: NodeData, includeChildren: boolean = true) => { export const createNode = (data: NodeData, includeChildren: boolean = true) => {
const node: NodeData = { const node: NodeData = {
$id: data.$id,
id: data.id, id: data.id,
name: data.name, name: data.name,
desc: data.desc, desc: data.desc,
@@ -598,7 +599,7 @@ const appendStatusFlag = (status: number, childStatus: number) => {
}; };
const buildStatusFlag = (data: NodeData, childStatus: number) => { const buildStatusFlag = (data: NodeData, childStatus: number) => {
let status = data.status!; let status = data.$status!;
const def = nodeDefs.get(data.name); const def = nodeDefs.get(data.name);
if (def.status?.length) { if (def.status?.length) {
const childSuccess = (childStatus >> StatusFlag.SUCCESS) & 1; const childSuccess = (childStatus >> StatusFlag.SUCCESS) & 1;
@@ -639,9 +640,9 @@ const buildStatusFlag = (data: NodeData, childStatus: number) => {
break; break;
} }
}); });
data.status = status; data.$status = status;
} else { } else {
data.status = status | childStatus; data.$status = status | childStatus;
} }
}; };
@@ -694,9 +695,9 @@ export const checkTreeData = (data: NodeData) => {
return true; return true;
}; };
export const refreshNodeData = (node: NodeData, id: number) => { export const refreshNodeData = (tree: TreeData, node: NodeData, id: number) => {
node.id = (id++).toString(); node.id = (id++).toString();
node.size = calcSize(node); node.$size = calcSize(node);
const def = nodeDefs.get(node.name); const def = nodeDefs.get(node.name);
@@ -718,16 +719,16 @@ export const refreshNodeData = (node: NodeData, id: number) => {
parsingStack.push(node.path); parsingStack.push(node.path);
try { try {
const subtreePath = workdir + "/" + node.path; const subtreePath = workdir + "/" + node.path;
const subtree = readTree(subtreePath).root; const subtree = readTree(subtreePath);
id = refreshNodeData(subtree, --id); id = refreshNodeData(subtree, subtree.root, --id);
node.name = subtree.name; node.name = subtree.name;
node.desc = subtree.desc; node.desc = subtree.desc;
node.args = subtree.args; node.args = subtree.root.args;
node.input = subtree.input; node.input = subtree.root.input;
node.output = subtree.output; node.output = subtree.root.output;
node.children = subtree.children; node.children = subtree.root.children;
node.mtime = fs.statSync(subtreePath).mtimeMs; node.$mtime = fs.statSync(subtreePath).mtimeMs;
node.size = calcSize(node); node.$size = calcSize(node);
} catch (e) { } catch (e) {
alertError(`解析子树失败:${node.path}`); alertError(`解析子树失败:${node.path}`);
console.log("parse subtree:", e); console.log("parse subtree:", e);
@@ -735,16 +736,16 @@ export const refreshNodeData = (node: NodeData, id: number) => {
parsingStack.pop(); parsingStack.pop();
} else if (node.children?.length) { } else if (node.children?.length) {
for (let i = 0; i < node.children.length; i++) { 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) { if (node.children) {
let childStatus = 0; let childStatus = 0;
node.children.forEach((child) => { node.children.forEach((child) => {
if (child.status && !child.disabled) { if (child.$status && !child.disabled) {
childStatus = appendStatusFlag(childStatus, child.status); childStatus = appendStatusFlag(childStatus, child.$status);
} }
}); });
buildStatusFlag(node, childStatus); buildStatusFlag(node, childStatus);
@@ -756,7 +757,7 @@ export const refreshNodeData = (node: NodeData, id: number) => {
export const createBuildData = (path: string) => { export const createBuildData = (path: string) => {
try { try {
const treeModel: TreeData = readTree(path); const treeModel: TreeData = readTree(path);
refreshNodeData(treeModel.root, 1); refreshNodeData(treeModel, treeModel.root, 1);
dfs(treeModel.root, (node) => (node.id = treeModel.prefix + node.id)); dfs(treeModel.root, (node) => (node.id = treeModel.prefix + node.id));
treeModel.name = Path.basenameWithoutExt(path); treeModel.name = Path.basenameWithoutExt(path);
treeModel.root = createFileData(treeModel.root, true); treeModel.root = createFileData(treeModel.root, true);
@@ -885,6 +886,7 @@ export const loadModule = async (path: string) => {
export const createFileData = (data: NodeData, includeSubtree?: boolean) => { export const createFileData = (data: NodeData, includeSubtree?: boolean) => {
const nodeData: NodeData = { const nodeData: NodeData = {
$id: data.$id,
id: data.id, id: data.id,
name: data.name, name: data.name,
desc: data.desc || undefined, desc: data.desc || undefined,
@@ -926,7 +928,9 @@ export const createNewTree = (path: string) => {
root: { root: {
id: "1", id: "1",
name: "Sequence", name: "Sequence",
$id: nanoid(),
}, },
$override: {},
}; };
return tree; return tree;
}; };
+16 -4
View File
@@ -1,9 +1,15 @@
import * as fs from "fs"; import * as fs from "fs";
import { customAlphabet } from "nanoid";
import { type WorkspaceModel } from "../contexts/workspace-context"; 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 { dfs } from "./b3util";
import Path from "./path"; import Path from "./path";
export const nanoid = customAlphabet(
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
10
);
export const readJson = <T>(path: string): T => { export const readJson = <T>(path: string): T => {
const str = fs.readFileSync(path, "utf-8"); const str = fs.readFileSync(path, "utf-8");
return JSON.parse(str); return JSON.parse(str);
@@ -21,16 +27,21 @@ export const readWorkspace = (path: string) => {
}; };
export const readTree = (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.version = data.version ?? VERSION;
data.prefix = data.prefix ?? ""; data.prefix = data.prefix ?? "";
data.group = data.group || []; data.group = data.group || [];
data.import = data.import || []; data.import = data.import || [];
data.vars = data.vars || data.declvar || []; data.vars = data.vars || [];
data.root = data.root || {}; data.root = data.root || {};
// compatible with old version // 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; return data;
}; };
@@ -46,6 +57,7 @@ export const writeTree = (path: string, data: TreeData) => {
import: data.import, import: data.import,
vars: data.vars, vars: data.vars,
root: data.root, root: data.root,
$override: data.$override,
}); });
}; };