reimplement the editor use lastest antd and other libs, feature of this commit:
1. file manger 2. highlight for click input or output 3. selector of subtress 4. autocomplete for input or output
This commit is contained in:
1 parent
91da6f0071
commit
2e5c08b2ad
84 files changed
+14250
-11038
No files matched your search
@@ -0,0 +1,67 @@
|
||||
import { TreeGraphData as G6TreeGraphData } from "@antv/g6";
|
||||
|
||||
export interface NodeArgOption {
|
||||
name: string;
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
export interface NodeArg {
|
||||
name: string;
|
||||
type: string;
|
||||
desc: string;
|
||||
default?: string;
|
||||
options?: NodeArgOption[];
|
||||
}
|
||||
export interface NodeDef {
|
||||
name: string;
|
||||
type?: string;
|
||||
desc?: string;
|
||||
args?: NodeArg[];
|
||||
input?: string[];
|
||||
output?: string[];
|
||||
doc?: string;
|
||||
color?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface NodeModel {
|
||||
id: number;
|
||||
name: string;
|
||||
desc?: string;
|
||||
args?: { [key: string]: any };
|
||||
input?: string[];
|
||||
output?: string[];
|
||||
children?: NodeModel[];
|
||||
debug?: boolean;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface TreeModel {
|
||||
name: string;
|
||||
desc?: string;
|
||||
root: NodeModel;
|
||||
}
|
||||
|
||||
export interface TreeGraphData extends G6TreeGraphData {
|
||||
name: string;
|
||||
desc?: string;
|
||||
args?: { [key: string]: any };
|
||||
input?: string[];
|
||||
output?: string[];
|
||||
children?: TreeGraphData[];
|
||||
def: NodeDef;
|
||||
debug?: boolean;
|
||||
parent?: string;
|
||||
path?: string;
|
||||
lastModified?: number;
|
||||
|
||||
size?: number[];
|
||||
highlightInput?: boolean;
|
||||
highlightOutput?: boolean;
|
||||
}
|
||||
|
||||
export const unknownNodeDef: NodeDef = {
|
||||
name: "unknown",
|
||||
desc: "新建节点",
|
||||
type: "Action",
|
||||
};
|
||||
@@ -0,0 +1,439 @@
|
||||
import { useWorkspace } from "@/contexts/workspace-context";
|
||||
import { NodeModel, TreeGraphData, TreeModel } from "@/misc/b3type";
|
||||
import * as fs from "fs";
|
||||
import { message } from "./hooks";
|
||||
import Path from "./path";
|
||||
import i18n from "./i18n";
|
||||
|
||||
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 &&
|
||||
node1.desc === node2.desc &&
|
||||
node1.path === node2.path &&
|
||||
node1.debug === node2.debug
|
||||
) {
|
||||
const def = useWorkspace.getState().getNodeDef(node1.name);
|
||||
|
||||
for (const arg of def.args ?? []) {
|
||||
if (node1.args?.[arg.name] !== node2.args?.[arg.name]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (def.input?.length) {
|
||||
for (let i = 0; i < def.input.length; i++) {
|
||||
if (node1.input?.[i] !== node2.input?.[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (def.output?.length) {
|
||||
for (let i = 0; i < def.output.length; i++) {
|
||||
if (node1.output?.[i] !== node2.output?.[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const copyFromNode = (data: TreeGraphData, node: NodeModel) => {
|
||||
data.name = node.name;
|
||||
data.debug = node.debug;
|
||||
data.desc = node.desc;
|
||||
data.path = node.path;
|
||||
data.args = node.args;
|
||||
data.input = node.input;
|
||||
data.output = node.output;
|
||||
};
|
||||
|
||||
const parsingStack: string[] = [];
|
||||
|
||||
export const createNode = (data: TreeGraphData, includeChildren: boolean = true) => {
|
||||
const node: NodeModel = {
|
||||
id: Number(data.id),
|
||||
name: data.name,
|
||||
desc: data.desc,
|
||||
path: data.path,
|
||||
debug: data.debug,
|
||||
};
|
||||
if (data.input) {
|
||||
node.input = [];
|
||||
for (const v of data.input) {
|
||||
node.input.push(v ?? "");
|
||||
}
|
||||
}
|
||||
if (data.output) {
|
||||
node.output = [];
|
||||
for (const v of data.output) {
|
||||
node.output.push(v ?? "");
|
||||
}
|
||||
}
|
||||
if (data.args) {
|
||||
node.args = {};
|
||||
for (const k in data.args) {
|
||||
const v = data.args[k];
|
||||
if (v !== null && v !== undefined) {
|
||||
node.args[k] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.children && !isSubtreeRoot(data) && includeChildren) {
|
||||
node.children = [];
|
||||
for (const child of data.children) {
|
||||
node.children.push(createNode(child));
|
||||
}
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
export const refreshTreeDataId = (data: TreeGraphData, id?: number) => {
|
||||
if (!id) {
|
||||
id = 1;
|
||||
}
|
||||
data.id = (id++).toString();
|
||||
if (data.children) {
|
||||
data.children.forEach((child) => {
|
||||
child.parent = data.id;
|
||||
id = refreshTreeDataId(child, id);
|
||||
});
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
export const calcTreeDataSize = (data: TreeGraphData) => {
|
||||
let height = 40 + 2;
|
||||
const updateHeight = (obj: any) => {
|
||||
if (Array.isArray(obj) || (obj && Object.keys(obj).length > 0)) {
|
||||
const { str, line } = toBreakWord(`参数:${JSON.stringify(obj)}`, 35);
|
||||
height += 20 * line;
|
||||
}
|
||||
};
|
||||
if (data.path) {
|
||||
height += 20;
|
||||
}
|
||||
updateHeight(data.args);
|
||||
updateHeight(data.input);
|
||||
updateHeight(data.output);
|
||||
return [200, height];
|
||||
};
|
||||
|
||||
export const createTreeData = (node: NodeModel, parent?: string) => {
|
||||
const workspace = useWorkspace.getState();
|
||||
let treeData: TreeGraphData = {
|
||||
id: node.id.toFixed(),
|
||||
name: node.name,
|
||||
desc: node.desc,
|
||||
args: node.args,
|
||||
input: node.input,
|
||||
output: node.output,
|
||||
debug: node.debug,
|
||||
def: workspace.getNodeDef(node.name),
|
||||
parent: parent,
|
||||
};
|
||||
|
||||
treeData.size = calcTreeDataSize(treeData);
|
||||
|
||||
if (!parent) {
|
||||
parsingStack.length = 0;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
treeData.children = [];
|
||||
node.children.forEach((child) => {
|
||||
treeData.children!.push(createTreeData(child, treeData.id));
|
||||
});
|
||||
} else if (node.path) {
|
||||
if (parsingStack.indexOf(node.path) >= 0) {
|
||||
treeData.path = node.path;
|
||||
treeData.size = calcTreeDataSize(treeData);
|
||||
message.error(`循环引用节点:${node.path}`, 4);
|
||||
return treeData;
|
||||
}
|
||||
parsingStack.push(node.path);
|
||||
try {
|
||||
const subtreePath = workspace.workdir + "/" + node.path;
|
||||
const str = fs.readFileSync(subtreePath, "utf8");
|
||||
treeData = createTreeData(JSON.parse(str).root, treeData.id);
|
||||
treeData.lastModified = fs.statSync(subtreePath).mtimeMs;
|
||||
treeData.path = node.path;
|
||||
treeData.debug = node.debug;
|
||||
treeData.size = calcTreeDataSize(treeData);
|
||||
} catch (error) {
|
||||
message.error(`解析子树失败:${node.path}`);
|
||||
console.log("parse subtree:", error);
|
||||
}
|
||||
parsingStack.pop();
|
||||
}
|
||||
calcTreeDataSize(treeData);
|
||||
return treeData;
|
||||
};
|
||||
|
||||
export const createBuildData = (path: string) => {
|
||||
try {
|
||||
const str = fs.readFileSync(path, "utf8");
|
||||
const treeModel = JSON.parse(str);
|
||||
const data = createTreeData(treeModel.root);
|
||||
refreshTreeDataId(data);
|
||||
treeModel.root = createFileData(data, true);
|
||||
return treeModel as NodeModel;
|
||||
} catch (error) {
|
||||
console.log("build error:", path, error);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const createFileData = (data: TreeGraphData, includeSubtree?: boolean) => {
|
||||
const nodeData: NodeModel = {
|
||||
id: Number(data.id),
|
||||
name: data.name,
|
||||
desc: data.desc || undefined,
|
||||
args: data.args || undefined,
|
||||
input: data.input || undefined,
|
||||
output: data.output || undefined,
|
||||
debug: data.debug || undefined,
|
||||
path: data.path || undefined,
|
||||
};
|
||||
if (data.children?.length && (includeSubtree || !isSubtreeRoot(data))) {
|
||||
nodeData.children = [];
|
||||
data.children.forEach((child) => {
|
||||
nodeData.children!.push(createFileData(child, includeSubtree));
|
||||
});
|
||||
}
|
||||
return nodeData;
|
||||
};
|
||||
|
||||
export const createNewTree = (filename: string) => {
|
||||
const tree: TreeModel = {
|
||||
name: Path.basenameWithoutExt(filename),
|
||||
root: {
|
||||
id: 1,
|
||||
name: "Sequence",
|
||||
},
|
||||
};
|
||||
return tree;
|
||||
};
|
||||
|
||||
export const toBreakWord = (str: string, len: number, char = "\n") => {
|
||||
let strTemp = "";
|
||||
let line = 1;
|
||||
if (str.length <= len) {
|
||||
return { str, line };
|
||||
}
|
||||
while (str.length > len) {
|
||||
strTemp += str.slice(0, len) + char;
|
||||
str = str.slice(len, str.length);
|
||||
line++;
|
||||
}
|
||||
strTemp += str;
|
||||
return {
|
||||
str: strTemp,
|
||||
line,
|
||||
};
|
||||
};
|
||||
|
||||
export const createProject = (path: string) => {
|
||||
fs.writeFileSync(
|
||||
Path.dirname(path) + "/node-config.b3-setting",
|
||||
JSON.stringify(
|
||||
[
|
||||
{
|
||||
name: "AlwaysFail",
|
||||
type: "Decorator",
|
||||
desc: i18n.t("node.alwaysFail.desc"),
|
||||
doc: i18n.t("node.alwaysFail.doc"),
|
||||
},
|
||||
{
|
||||
name: "AlwaysSuccess",
|
||||
type: "Decorator",
|
||||
desc: i18n.t("node.alwaysSuccess.desc"),
|
||||
doc: i18n.t("node.alwaysSuccess.doc"),
|
||||
},
|
||||
{
|
||||
name: "Check",
|
||||
type: "Condition",
|
||||
desc: i18n.t("node.check.desc"),
|
||||
args: [
|
||||
{
|
||||
name: "value",
|
||||
type: "code",
|
||||
desc: i18n.t("node.check.arg1.desc"),
|
||||
},
|
||||
],
|
||||
doc: i18n.t("node.check.doc"),
|
||||
},
|
||||
{
|
||||
name: "Clear",
|
||||
type: "Action",
|
||||
desc: i18n.t("node.clear.desc"),
|
||||
output: [i18n.t("node.clear.output1")],
|
||||
},
|
||||
{
|
||||
name: "ForEach",
|
||||
type: "Composite",
|
||||
desc: i18n.t("node.forEach.desc"),
|
||||
input: [i18n.t("node.forEach.input1")],
|
||||
output: [i18n.t("node.forEach.output1")],
|
||||
doc: i18n.t("node.forEach.doc"),
|
||||
},
|
||||
{
|
||||
name: "GetTime",
|
||||
type: "Action",
|
||||
desc: i18n.t("node.getTime.desc"),
|
||||
output: [i18n.t("node.getTime.output1")],
|
||||
},
|
||||
{
|
||||
name: "IsNull",
|
||||
type: "Condition",
|
||||
desc: i18n.t("node.isNull.desc"),
|
||||
input: [i18n.t("node.isNull.input1")],
|
||||
},
|
||||
{
|
||||
name: "Log",
|
||||
type: "Action",
|
||||
desc: i18n.t("node.log.desc"),
|
||||
args: [
|
||||
{
|
||||
name: "message",
|
||||
type: "string",
|
||||
desc: i18n.t("node.log.arg1.desc"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Loop",
|
||||
type: "Composite",
|
||||
desc: i18n.t("node.loop.desc"),
|
||||
args: [
|
||||
{
|
||||
name: "count",
|
||||
type: "int?",
|
||||
desc: i18n.t("node.loop.arg1.desc"),
|
||||
},
|
||||
],
|
||||
input: [i18n.t("node.loop.input1") + "?"],
|
||||
},
|
||||
{
|
||||
name: "Not",
|
||||
type: "Decorator",
|
||||
desc: i18n.t("node.not.desc"),
|
||||
doc: i18n.t("node.not.doc"),
|
||||
},
|
||||
{
|
||||
name: "NotNull",
|
||||
type: "Condition",
|
||||
desc: i18n.t("node.notNull.desc"),
|
||||
input: [i18n.t("node.notNull.input")],
|
||||
},
|
||||
{
|
||||
name: "Once",
|
||||
type: "Composite",
|
||||
desc: i18n.t("node.once.desc"),
|
||||
doc: i18n.t("node.once.doc"),
|
||||
},
|
||||
{
|
||||
name: "Parallel",
|
||||
type: "Composite",
|
||||
desc: i18n.t("node.parallel.desc"),
|
||||
doc: i18n.t("node.parallel.doc"),
|
||||
},
|
||||
{
|
||||
name: "Selector",
|
||||
type: "Composite",
|
||||
desc: i18n.t("node.selector.desc"),
|
||||
doc: i18n.t("node.selector.doc"),
|
||||
},
|
||||
{
|
||||
name: "Sequence",
|
||||
type: "Composite",
|
||||
desc: i18n.t("node.sequence.desc"),
|
||||
doc: i18n.t("node.sequence.doc"),
|
||||
},
|
||||
{
|
||||
name: "Wait",
|
||||
type: "Action",
|
||||
desc: i18n.t("node.wait.desc"),
|
||||
args: [
|
||||
{
|
||||
name: "time",
|
||||
type: "float",
|
||||
desc: i18n.t("node.wait.arg1.desc"),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
fs.writeFileSync(
|
||||
Path.dirname(path) + "/example.json",
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "example",
|
||||
root: {
|
||||
id: 1,
|
||||
name: "Sequence",
|
||||
children: [
|
||||
{
|
||||
id: 2,
|
||||
name: "Log",
|
||||
args: {
|
||||
str: "hello",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Wait",
|
||||
args: {
|
||||
time: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path,
|
||||
JSON.stringify(
|
||||
{
|
||||
nodeConf: "node-config.b3-setting",
|
||||
metadata: [],
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { App } from "antd";
|
||||
import type { MessageInstance } from "antd/es/message/interface";
|
||||
import type { ModalStaticFunctions } from "antd/es/modal/confirm";
|
||||
import type { NotificationInstance } from "antd/es/notification/interface";
|
||||
|
||||
let message: MessageInstance;
|
||||
let notification: NotificationInstance;
|
||||
let modal: Omit<ModalStaticFunctions, "warn">;
|
||||
|
||||
const setGlobalHooks = () => {
|
||||
const staticFunction = App.useApp();
|
||||
message = staticFunction.message;
|
||||
modal = staticFunction.modal;
|
||||
notification = staticFunction.notification;
|
||||
};
|
||||
|
||||
export { message, modal, notification, setGlobalHooks };
|
||||
@@ -0,0 +1,45 @@
|
||||
import dayjs from "dayjs";
|
||||
import i18n from "i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import HttpApi from "i18next-http-backend";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
declare module "i18next" {
|
||||
interface CustomTypeOptions {
|
||||
returnNull: false;
|
||||
}
|
||||
}
|
||||
|
||||
i18n
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.use(HttpApi)
|
||||
.init(
|
||||
{
|
||||
debug: import.meta.env.NODE_ENV === "development",
|
||||
returnNull: false,
|
||||
fallbackLng: "en",
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
load: "languageOnly",
|
||||
detection: {
|
||||
caches: ["localStorage", "sessionStorage", "cookie"],
|
||||
},
|
||||
backend: {
|
||||
loadPath: `locales/{{lng}}.json`,
|
||||
},
|
||||
},
|
||||
function (err, t) {
|
||||
dayjs.locale(i18n.language);
|
||||
}
|
||||
);
|
||||
|
||||
i18n.on("languageChanged", (lng) => {
|
||||
dayjs.locale(lng);
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@@ -0,0 +1,11 @@
|
||||
import * as fs from "fs";
|
||||
|
||||
export const readJson = (path: string) => {
|
||||
const str = fs.readFileSync(path, "utf-8");
|
||||
return JSON.parse(str);
|
||||
};
|
||||
|
||||
export const writeJson = (path: string, data: any) => {
|
||||
const str = JSON.stringify(data, undefined, 2);
|
||||
fs.writeFileSync(path, str, "utf-8");
|
||||
};
|
||||
@@ -0,0 +1,105 @@
|
||||
import { BrowserWindow } from "@electron/remote";
|
||||
import { MutableRefObject } from "react";
|
||||
import {
|
||||
HotkeyCallback,
|
||||
Keys,
|
||||
isHotkeyPressed as _isHotkeyPressed,
|
||||
useHotkeys as _useHotkeys,
|
||||
} from "react-hotkeys-hook";
|
||||
import { OptionsOrDependencyArray, RefType } from "react-hotkeys-hook/dist/types";
|
||||
import { Key } from "ts-key-enum";
|
||||
|
||||
export const isMacos = process.platform === "darwin";
|
||||
|
||||
let forceTarget: HTMLElement | undefined;
|
||||
|
||||
const hotkey = (key: string) => {
|
||||
if (key.indexOf("ctrl") >= 0 && isMacos) {
|
||||
key = key.replace("ctrl", Key.Meta);
|
||||
}
|
||||
return key.toLowerCase();
|
||||
};
|
||||
|
||||
export const isHotkeyPressed = (key: string) => {
|
||||
if (!key) {
|
||||
return false;
|
||||
} else if (key.indexOf("ctrl") >= 0 && isMacos) {
|
||||
key = key.replace("ctrl", Key.Meta);
|
||||
}
|
||||
return _isHotkeyPressed(key.toLowerCase(), "+");
|
||||
};
|
||||
|
||||
export function useHotkeys<T extends HTMLElement>(
|
||||
keys: Keys,
|
||||
callback: HotkeyCallback,
|
||||
options?: OptionsOrDependencyArray,
|
||||
dependencies?: OptionsOrDependencyArray
|
||||
): MutableRefObject<RefType<T>> {
|
||||
if (keys instanceof Array) {
|
||||
keys = keys.filter((v) => !!v);
|
||||
}
|
||||
return _useHotkeys(keys, callback, options, dependencies);
|
||||
}
|
||||
|
||||
export const Hotkey = {
|
||||
Backspace: Key.Backspace,
|
||||
Build: hotkey("ctrl+b"),
|
||||
CloseEditor: hotkey("ctrl+w"),
|
||||
Copy: hotkey("ctrl+c"),
|
||||
Delete: Key.Delete,
|
||||
Duplicate: hotkey("ctrl+d"),
|
||||
Enter: Key.Enter,
|
||||
Escape: Key.Escape,
|
||||
F2: Key.F2,
|
||||
Insert: Key.Insert,
|
||||
MacDelete: isMacos ? hotkey("ctrl+backspace") : "",
|
||||
Paste: hotkey("ctrl+v"),
|
||||
Redo: isMacos ? hotkey("shift+ctrl+z") : hotkey("ctrl+y"),
|
||||
Replace: hotkey("shift+ctrl+v"),
|
||||
Save: hotkey("ctrl+s"),
|
||||
Search: hotkey("ctrl+p"),
|
||||
Undo: hotkey("ctrl+z"),
|
||||
};
|
||||
|
||||
export const setInputFocus = (target: HTMLElement) => {
|
||||
forceTarget = target;
|
||||
};
|
||||
|
||||
export const sendInputEvent = (key: string) => {
|
||||
const webContents = BrowserWindow.getFocusedWindow()?.webContents;
|
||||
if (webContents) {
|
||||
let keyCode: string | undefined;
|
||||
const modifiers: Electron.InputEvent["modifiers"] = [];
|
||||
key.split("+").forEach((v) => {
|
||||
if (v.indexOf("ctrl") >= 0) {
|
||||
modifiers.push("ctrl");
|
||||
} else if (v.indexOf("shift") >= 0) {
|
||||
modifiers.push("shift");
|
||||
} else if (v.indexOf("alt") >= 0) {
|
||||
modifiers.push("alt");
|
||||
} else if (v.indexOf("meta") >= 0) {
|
||||
modifiers.push("meta");
|
||||
} else {
|
||||
keyCode = v;
|
||||
}
|
||||
});
|
||||
if (!keyCode) {
|
||||
return;
|
||||
}
|
||||
forceTarget?.focus();
|
||||
modifiers.forEach((mod) => {
|
||||
webContents.sendInputEvent({
|
||||
type: "keyDown",
|
||||
keyCode: mod,
|
||||
});
|
||||
});
|
||||
webContents.sendInputEvent({ type: "keyDown", keyCode: keyCode, modifiers: modifiers });
|
||||
webContents.sendInputEvent({ type: "keyUp", keyCode: keyCode, modifiers: modifiers });
|
||||
modifiers.forEach((mod) => {
|
||||
webContents.sendInputEvent({
|
||||
type: "keyUp",
|
||||
keyCode: mod,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import path from "path";
|
||||
|
||||
const Path = path;
|
||||
|
||||
declare module "path" {
|
||||
interface PlatformPath {
|
||||
basenameWithoutExt(path: string): string;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
path.basenameWithoutExt = (path: string) => {
|
||||
return Path.basename(path, Path.extname(path));
|
||||
};
|
||||
|
||||
export default Path;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ThemeConfig, theme } from "antd";
|
||||
import "../index.scss";
|
||||
|
||||
export const themeConfig: ThemeConfig = {
|
||||
cssVar: true,
|
||||
algorithm: theme.darkAlgorithm,
|
||||
token: {
|
||||
colorBgBase: "#0d1117",
|
||||
colorBgContainer: "#0d1117",
|
||||
colorBgElevated: "#161b22",
|
||||
colorBorderSecondary: "#30363d",
|
||||
borderRadius: 4,
|
||||
},
|
||||
components: {
|
||||
Tree: {
|
||||
// directoryNodeSelectedBg: "#010409",
|
||||
borderRadius: 0,
|
||||
colorBgContainer: "#010409",
|
||||
},
|
||||
Tabs: {
|
||||
horizontalMargin: "0",
|
||||
},
|
||||
Layout: {
|
||||
headerBg: "#0d1117",
|
||||
siderBg: "#010409",
|
||||
},
|
||||
Dropdown: {
|
||||
motionDurationMid: "0.1s",
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in new issue
Block a user