diff --git a/app/main/handlers/manageYakScript.js b/app/main/handlers/manageYakScript.js index a2b8926e03..34b0cc6aaf 100644 --- a/app/main/handlers/manageYakScript.js +++ b/app/main/handlers/manageYakScript.js @@ -349,7 +349,20 @@ module.exports = (win, getClient) => { }) }) - // 读取本地文件内容 + // 读取本地文件大小 + ipcMain.handle("read-file-size", async (e,filePath) => { + return new Promise((resolve, reject) => { + fs.stat(filePath, (err, stats) => { + if (err) { + reject(err); + } else { + resolve(stats.size); // 文件大小以字节为单位 + } + }); + }) + }) + + // 读取本地文件内容(同时校验其文件大小是否读取本地文件10M,大于则不读取) ipcMain.handle("read-file-content", async (e,params) => { console.log("read-file-content",params); return new Promise((resolve, reject) => { diff --git a/app/main/handlers/yakRunnerTerminal.js b/app/main/handlers/yakRunnerTerminal.js index e2c3a50c22..b7f16d26fb 100644 --- a/app/main/handlers/yakRunnerTerminal.js +++ b/app/main/handlers/yakRunnerTerminal.js @@ -2,73 +2,70 @@ const {ipcMain, clipboard} = require("electron"); module.exports = (win, getClient) => { - // let streams = {}; - // const getStreamByPort = (addr) => { - // return streams[addr]; - // } - // const removeStreamPort = (addr) => { - // const stream = streams[addr]; - // if (stream) { - // stream.cancel(); - // delete streams[addr]; - // } - // }; + let streams = {}; + const getStreamByPort = (path) => { + return streams[path]; + } + const removeStreamPort = (path) => { + // const stream = streams[path]; + // if (stream) { + // stream.cancel(); + // delete streams[path]; + // } + }; - // ipcMain.handle("runner-terminal-query-addrs", () => { - // return Object.keys(streams).map(i => `${i}`) - // }); - // ipcMain.handle("runner-terminal-input", async (e, addr, data) => { - // const stream = getStreamByPort(addr); - // if (stream) { - // stream.write({ - // raw: Buffer.from(data, "utf8") - // }) - // } - // }) - // ipcMain.handle("runner-terminal-cancel", async (e, addr) => { - // removeStreamPort(addr) - // }); - // ipcMain.handle("runner-terminal-port", async (e, host, port) => { - // const addr = `${host}:${port}` - // if (getStreamByPort(addr)) { - // throw Error("listened port"); - // } - // stream = getClient().YaklangTerminal(); - // // 如果有问题,重置 - // stream.on("error", (e) => { - // removeStreamPort(addr) - // }) + ipcMain.handle("runner-terminal-query-addrs", () => { + return Object.keys(streams).map(i => `${i}`) + }); + ipcMain.handle("runner-terminal-input", async (e, path, data) => { + const stream = getStreamByPort(path); + if (stream) { + stream.write({ + raw: Buffer.from(data, "utf8") + }) + } + }) + ipcMain.handle("runner-terminal-cancel", async (e, path) => { + removeStreamPort(path) + }); + ipcMain.handle("runner-terminal", async (e, params) => { + const {path} = params + if (getStreamByPort(path)) { + throw Error("listened terminal"); + } + stream = getClient().YaklangTerminal(); + // 如果有问题,重置 + stream.on("error", (e) => { + console.log("error---",e); + removeStreamPort(path) + }) - // // 发送回数据 - // stream.on("data", data => { - // if (data.control) { - // if (win && data.waiting) { - // win.webContents.send(`client-listening-port-success-${addr}`) - // } - // if (win && data.closed) { - // removeStreamPort(addr) - // } - // return - // } + // 发送回数据 + stream.on("data", data => { + console.log("data---",e); + if (data.control) { + if (win && data.waiting) { + win.webContents.send(`client-listening-terminal-success-${path}`) + } + if (win && data.closed) { + removeStreamPort(path) + } + return + } - // if (win) { - // win.webContents.send(`client-listening-port-data-${addr}`, data) - // } - // }) - // stream.on("end", () => { - // removeStreamPort(addr) - // if (win) { - // win.webContents.send("client-listening-port-end", addr); - // } - // }) - // stream.write({ - // host, port, - // }) - // streams[addr] = stream; - // }) - - - // ipcMain.handle("copy-clipboard", (e, text) => { - // clipboard.writeText(text); - // }); + if (win) { + win.webContents.send(`client-listening-terminal-data-${path}`, data) + } + }) + stream.on("end", () => { + console.log("end---",e); + removeStreamPort(path) + if (win) { + win.webContents.send("client-listening-terminal-end", path); + } + }) + console.log("start---",params); + stream.write(params) + streams[path] = stream; + }) } \ No newline at end of file diff --git a/app/protos/grpc.proto b/app/protos/grpc.proto index 62adad7bf7..562e455c87 100644 --- a/app/protos/grpc.proto +++ b/app/protos/grpc.proto @@ -136,6 +136,8 @@ service Yak { // 开启端口 rpc OpenPort(stream Input) returns (stream Output); + // editor terminal 交互 + rpc YaklangTerminal(stream Input) returns (stream Output); // Exec rpc Exec(ExecRequest) returns (stream ExecResult); @@ -4859,6 +4861,7 @@ message Input { string host = 2; uint32 port = 3; + string path = 4; } message Output { diff --git a/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/BottomEditorDetails.tsx b/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/BottomEditorDetails.tsx index be0ad2e118..c7ec48d554 100644 --- a/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/BottomEditorDetails.tsx +++ b/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/BottomEditorDetails.tsx @@ -23,12 +23,21 @@ import {defaultXTermOptions} from "@/components/baseConsole/BaseConsole" import {XTerm} from "xterm-for-react" import {YakitSystem} from "@/yakitGVDefine" import {TerminalBox} from "./TerminalBox/TerminalBox" +import {System, SystemInfo, handleFetchSystem} from "@/constants/hardware" const {ipcRenderer} = window.require("electron") // 编辑器区域 展示详情(输出/语法检查/终端/帮助信息) export const BottomEditorDetails: React.FC = (props) => { const {setEditorDetails, showItem, setShowItem} = props + + const systemRef = useRef(SystemInfo.system) + useEffect(() => { + if (!systemRef.current) { + handleFetchSystem(() => (systemRef.current = SystemInfo.system)) + } + }, []) + const {activeFile} = useStore() // 不再重新加载的元素 const [showType, setShowType] = useState([]) @@ -181,7 +190,11 @@ export const BottomEditorDetails: React.FC = (props) = [styles["render-show"]]: showItem === "terminal" })} > - + {systemRef.current === "Windows_NT" ? ( +
终端监修中
+ ) : ( + + )} )} {/* 帮助信息只有yak有 */} diff --git a/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/TerminalBox/TerminalBox.module.scss b/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/TerminalBox/TerminalBox.module.scss index a28a7abbc8..fc73ce8678 100644 --- a/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/TerminalBox/TerminalBox.module.scss +++ b/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/TerminalBox/TerminalBox.module.scss @@ -1,4 +1,6 @@ .terminal-box{ width: 100%; height: 100%; + padding: 12px 0px 0px 12px; + background-color: rgb(49, 52, 63); } \ No newline at end of file diff --git a/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/TerminalBox/TerminalBox.tsx b/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/TerminalBox/TerminalBox.tsx index f5aeced949..2d29845d34 100644 --- a/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/TerminalBox/TerminalBox.tsx +++ b/app/renderer/src/main/src/pages/YakRunner/BottomEditorDetails/TerminalBox/TerminalBox.tsx @@ -1,5 +1,5 @@ -import React, {useEffect, useRef, useState} from "react" -import {} from "antd" +import React, {useEffect, useMemo, useRef, useState} from "react" +import {Modal} from "antd" import {} from "@ant-design/icons" import {useGetState, useMemoizedFn} from "ahooks" import {NetWorkApi} from "@/services/fetch" @@ -10,36 +10,121 @@ import classNames from "classnames" import ReactResizeDetector from "react-resize-detector" import {writeXTerm, xtermClear, xtermFit} from "@/utils/xtermUtils" import {TERMINAL_INPUT_KEY, YakitCVXterm} from "@/components/yakitUI/YakitCVXterm/YakitCVXterm" +import useStore from "../../hooks/useStore" +import {YakitHint} from "@/components/yakitUI/YakitHint/YakitHint" const {ipcRenderer} = window.require("electron") -export interface TerminalBoxProps {} +export interface TerminalBoxProps { + isShow: boolean +} export const TerminalBox: React.FC = (props) => { + const {isShow} = props + const {fileTree} = useStore() const xtermRef = useRef(null) const [inputValue, setInputValue] = useState("") const [defaultXterm, setDefaultXterm] = useState("") + // 是否允许输入及不允许输入的原因 + const [allowInput, setAllowInput] = useState(true) + const [failResult, setFailResult] = useState("") + const [showModal, setShowModal] = useState(false) + + // 终端path为文件树根路径 + const folderPath = useMemo(() => { + if (fileTree.length > 0) { + return fileTree[0].path + } else { + return "" + } + }, [fileTree]) + + // 写入 + const commandExec = useMemoizedFn((cmd) => { + if (!xtermRef || !xtermRef.current) { + return + } + if (cmd.startsWith(defaultXterm)) { + cmd = cmd.replace(defaultXterm, "") + } + console.log("写入", cmd) + + // const str = s.charCodeAt(0) === TERMINAL_INPUT_KEY.ENTER ? String.fromCharCode(10) : s + ipcRenderer.invoke("runner-terminal-input", folderPath, cmd) + }) useEffect(() => { if (!xtermRef) { return } - const params = {} + setAllowInput(true) + setFailResult("") + // 启动 ipcRenderer - .invoke("runner-terminal-port", params) + .invoke("runner-terminal", { + path: folderPath + }) .then(() => { success("终端监听成功") + if (folderPath.length) { + setDefaultXterm(`${folderPath}>`) + setInputValue(`${folderPath}>`) + writeXTerm(xtermRef, `${folderPath}>`) + } }) .catch((e: any) => { failed(`ERROR: ${JSON.stringify(e)}`) }) .finally(() => {}) - }, [xtermRef]) + + // 接收 + const key = `client-listening-terminal-data-${folderPath}` + ipcRenderer.on(key, (e, data) => { + console.log("data---", data) + + if (data.control) { + return + } + + // if (data?.raw && xtermRef?.current && xtermRef.current?.terminal) { + // // let str = String.fromCharCode.apply(null, data.raw); + // xtermRef.current.terminal.write(data.raw) + // setHaveConnIn(true) + // } + + // 先生成结果行 换行后 更改缓存新增输入行(结果行可能cd路径变化 需更改) + // writeXTerm(xtermRef, "result") + // writeXTerm(xtermRef, "\n") + + // setDefaultXterm(folderPath + ">") + // setInputValue(folderPath + ">") + // writeXTerm(xtermRef, folderPath + ">") + }) + + const successKey = `client-listening-terminal-success-${folderPath}` + ipcRenderer.on(successKey, (e: any) => { + console.log("client-listening-terminal-success---") + }) + + // grpc通知关闭 + const errorKey = "client-listening-terminal-end" + ipcRenderer.on(errorKey, (e: any, data: any) => { + setAllowInput(false) + setFailResult(data) + isShow&&setShowModal(true) + }) + return () => { + // 移除 + ipcRenderer.removeAllListeners(key) + ipcRenderer.removeAllListeners(successKey) + ipcRenderer.removeAllListeners(errorKey) + // 清空 + xtermClear(xtermRef) + } + }, [xtermRef, folderPath]) // xtermClear(xtermRef) // writeXTerm(xtermRef, defaultXterm) - const commandExec = useMemoizedFn((cmd: string) => {}) - return (
= (props) => { brightWhite: "#f6f7ec" } }} - isWrite={false} + // isWrite={false} onData={(data) => { + if (!allowInput) { + setShowModal(true) + return + } if (data.replace(/[\x7F]/g, "").length > 0) { writeXTerm(xtermRef, data) // 处理用户输入的数据 @@ -121,6 +210,17 @@ export const TerminalBox: React.FC = (props) => { } }} /> + {/* 终端关闭提示框 */} + { + setShowModal(false) + }} + okButtonText={"知道了"} + />
) } diff --git a/app/renderer/src/main/src/pages/YakRunner/CollapseList/CollapseList.tsx b/app/renderer/src/main/src/pages/YakRunner/CollapseList/CollapseList.tsx index 5fad24ee47..ea8dafae97 100644 --- a/app/renderer/src/main/src/pages/YakRunner/CollapseList/CollapseList.tsx +++ b/app/renderer/src/main/src/pages/YakRunner/CollapseList/CollapseList.tsx @@ -160,10 +160,11 @@ export const HelpInfoList: React.FC = memo((props) => { if (helpEditor) { const model = helpEditor.getModel() const position = activeFile?.position as monaco.Position + if (model && position) { const iWord = getWordWithPointAtPosition(model, position) const type = getModelContext(model, "plugin") || "yak" - + if (iWord.word.length === 0) return await ipcRenderer .invoke("YaklangLanguageFind", { InspectType: "reference", @@ -205,6 +206,7 @@ export const HelpInfoList: React.FC = memo((props) => { const position = activeFile?.position as monaco.Position if (model && position) { const iWord = getWordWithPointAtPosition(model, position) + if (iWord.word.length === 0) return const type = getModelContext(model, "plugin") || "yak" await ipcRenderer diff --git a/app/renderer/src/main/src/pages/YakRunner/FileTree/FileTree.module.scss b/app/renderer/src/main/src/pages/YakRunner/FileTree/FileTree.module.scss index d3250894e9..4b89293889 100644 --- a/app/renderer/src/main/src/pages/YakRunner/FileTree/FileTree.module.scss +++ b/app/renderer/src/main/src/pages/YakRunner/FileTree/FileTree.module.scss @@ -1,6 +1,7 @@ /* ---------- FileTree ----------*/ .file-tree { height: 100%; + padding: 0px 4px; :global { .ant-tree .ant-tree-treenode { padding-bottom: 0; @@ -27,6 +28,13 @@ .ant-tree-indent-unit{ width: 0px; } + // 树的滚动条 + .ant-tree-list-scrollbar{ + width: 6px !important; + } + .ant-tree-list-scrollbar-thumb{ + background: #c1c1c1 !important; + } } :global(.ant-tree-treenode-loading) { diff --git a/app/renderer/src/main/src/pages/YakRunner/RunnerFileTree/RunnerFileTree.module.scss b/app/renderer/src/main/src/pages/YakRunner/RunnerFileTree/RunnerFileTree.module.scss index 11c61ef641..f1941e2c52 100644 --- a/app/renderer/src/main/src/pages/YakRunner/RunnerFileTree/RunnerFileTree.module.scss +++ b/app/renderer/src/main/src/pages/YakRunner/RunnerFileTree/RunnerFileTree.module.scss @@ -43,7 +43,7 @@ overflow: hidden; .tree-body { height: 100%; - overflow: hidden auto; + overflow: hidden; } } } diff --git a/app/renderer/src/main/src/pages/YakRunner/RunnerFileTree/RunnerFileTree.tsx b/app/renderer/src/main/src/pages/YakRunner/RunnerFileTree/RunnerFileTree.tsx index bc8800b699..6b67becf2f 100644 --- a/app/renderer/src/main/src/pages/YakRunner/RunnerFileTree/RunnerFileTree.tsx +++ b/app/renderer/src/main/src/pages/YakRunner/RunnerFileTree/RunnerFileTree.tsx @@ -16,8 +16,10 @@ import {YakitDropdownMenu} from "@/components/yakitUI/YakitDropdownMenu/YakitDro import {YakitMenuItemType} from "@/components/yakitUI/YakitMenu/YakitMenu" import {FileDetailInfo} from "../RunnerTabs/RunnerTabsType" import { + MAX_FILE_SIZE_BYTES, addAreaFileInfo, getCodeByPath, + getCodeSizeByPath, getDefaultActiveFile, getNameByPath, getOpenFileInfo, @@ -58,6 +60,7 @@ import {v4 as uuidv4} from "uuid" import cloneDeep from "lodash/cloneDeep" import {failed, success} from "@/utils/notification" import {FileMonitorItemProps, FileMonitorProps} from "@/utils/duplex/duplex" +import { YakitHint } from "@/components/yakitUI/YakitHint/YakitHint" const {ipcRenderer} = window.require("electron") @@ -65,6 +68,7 @@ export const RunnerFileTree: React.FC = (props) => { const {addFileTab} = props const {fileTree, areaInfo, activeFile} = useStore() const {handleFileLoadData, setAreaInfo, setActiveFile, setFileTree} = useDispatcher() + const [isShowFileHint,setShowFileHint] = useState(false) const [historyList, setHistoryList] = useState([]) @@ -419,7 +423,6 @@ export const RunnerFileTree: React.FC = (props) => { const onRefreshYakRunnerFileTreeFun = useMemoizedFn((data) => { try { const event: FileMonitorProps = JSON.parse(data) - console.log("event---", event) if (event.ChangeEvents) { eventOperateFun(event.ChangeEvents) } @@ -507,6 +510,11 @@ export const RunnerFileTree: React.FC = (props) => { setAreaInfo && setAreaInfo(newAreaInfo) setActiveFile && setActiveFile(file) } else { + const size = await getCodeSizeByPath(path) + if(size > MAX_FILE_SIZE_BYTES){ + setShowFileHint(true) + return + } const code = await getCodeByPath(path) const suffix = name.indexOf(".") > -1 ? name.split(".").pop() : "" const scratchFile: FileDetailInfo = { @@ -658,6 +666,17 @@ export const RunnerFileTree: React.FC = (props) => { + {/* 文件过大提示框 */} + { + setShowFileHint(false) + }} + okButtonText={"知道了"} + /> ) } diff --git a/app/renderer/src/main/src/pages/YakRunner/RunnerTabs/RunnerTabs.tsx b/app/renderer/src/main/src/pages/YakRunner/RunnerTabs/RunnerTabs.tsx index 90cd4b8652..bfefc0bb14 100644 --- a/app/renderer/src/main/src/pages/YakRunner/RunnerTabs/RunnerTabs.tsx +++ b/app/renderer/src/main/src/pages/YakRunner/RunnerTabs/RunnerTabs.tsx @@ -44,8 +44,10 @@ import useDispatcher from "../hooks/useDispatcher" import {AreaInfoProps, TabFileProps, YakRunnerHistoryProps} from "../YakRunnerType" import {IMonacoEditor} from "@/utils/editors" import { + MAX_FILE_SIZE_BYTES, addAreaFileInfo, getCodeByPath, + getCodeSizeByPath, getDefaultActiveFile, getOpenFileInfo, getPathParent, @@ -78,6 +80,8 @@ import moment from "moment" import {YakitModal} from "@/components/yakitUI/YakitModal/YakitModal" import {getMapFileDetail, removeMapFileDetail, setMapFileDetail} from "../FileTreeMap/FileMap" import {getMapFolderDetail, setMapFolderDetail} from "../FileTreeMap/ChildMap" +import {YakitHint} from "@/components/yakitUI/YakitHint/YakitHint" +import {ExclamationCircleOutlined} from "@ant-design/icons" const {ipcRenderer} = window.require("electron") @@ -775,6 +779,11 @@ const RunnerTabBar: React.FC = memo((props) => { }, {wait: 200} ).run + + useUpdateEffect(()=>{ + onScrollTabMenu() + },[tabsList]) + return (
@@ -961,7 +970,7 @@ const RunnerTabPane: React.FC = memo((props) => { // 更新编辑器文件内容(activeFile-code字段在光标位置改变时就已更新,为减少渲染,则不更新) const updateAreaInputInfo = useMemoizedFn((content: string) => { // 未保存文件不用自动保存 - if(editorInfo?.isUnSave) return + if (editorInfo?.isUnSave) return const newAreaInfo = updateAreaFileInfo(areaInfo, {code: content}, editorInfo?.path) // console.log("更新编辑器文件内容", newAreaInfo) if (editorInfo) { @@ -1061,7 +1070,7 @@ const RunnerTabPane: React.FC = memo((props) => { // console.log("更新光标位置", editorInfo) if (reqEditor && editorInfo) { // 如若没有记录 默认1行1列 - const {position={lineNumber:1,column:1}, selections} = editorInfo + const {position = {lineNumber: 1, column: 1}, selections} = editorInfo const {lineNumber, column} = position if (lineNumber && column) { reqEditor.setPosition({lineNumber, column}) @@ -1127,6 +1136,8 @@ export const YakRunnerWelcomePage: React.FC = memo((p const {areaInfo, activeFile} = useStore() const {setAreaInfo, setActiveFile} = useDispatcher() + const [isShowFileHint, setShowFileHint] = useState(false) + const [historyList, setHistoryList] = useState([]) const getHistoryList = useMemoizedFn(async () => { @@ -1140,6 +1151,11 @@ export const YakRunnerWelcomePage: React.FC = memo((p // 通过路径打开文件 const openFileByPath = useMemoizedFn(async (path: string, name: string) => { // 由于此处的打开文件 不存在已打开文件 故无需校验 + const size = await getCodeSizeByPath(path) + if (size > MAX_FILE_SIZE_BYTES) { + setShowFileHint(true) + return + } const code = await getCodeByPath(path) const suffix = name.indexOf(".") > -1 ? name.split(".").pop() : "" const scratchFile: FileDetailInfo = { @@ -1259,6 +1275,17 @@ export const YakRunnerWelcomePage: React.FC = memo((p
+ {/* 文件过大提示框 */} + { + setShowFileHint(false) + }} + okButtonText={"知道了"} + /> ) }) @@ -1356,29 +1383,23 @@ export const YakitRunnerSaveModal: React.FC = (props) }) return ( - setShowModal(false)} - footer={null} - className={styles["yakit-runner-save-modal"]} - bodyStyle={{padding: 0}} - > -
是否要保存{info.name}里面的内容吗?
-
- - 取消 - - - 不保存 - - - 保存 - -
-
+ title={"文件未保存"} + content={`是否要保存${info.name}里面的内容吗?`} + footer={ +
+ + 取消 + + + 不保存 + + + 保存 + +
+ } + /> ) } diff --git a/app/renderer/src/main/src/pages/YakRunner/YakRunner.tsx b/app/renderer/src/main/src/pages/YakRunner/YakRunner.tsx index e9dfa6d5c8..56214285bb 100644 --- a/app/renderer/src/main/src/pages/YakRunner/YakRunner.tsx +++ b/app/renderer/src/main/src/pages/YakRunner/YakRunner.tsx @@ -1,5 +1,5 @@ import React, {useEffect, useMemo, useRef, useState} from "react" -import {useMemoizedFn, useUpdateEffect} from "ahooks" +import {useMemoizedFn, useThrottleFn, useUpdateEffect} from "ahooks" import {LeftSideBar} from "./LeftSideBar/LeftSideBar" import {BottomSideBar} from "./BottomSideBar/BottomSideBar" import {RightSideBar} from "./RightSideBar/RightSideBar" @@ -433,27 +433,30 @@ export const YakRunner: React.FC = (props) => { const keyDownRef = useRef(null) const unTitleCountRef = useRef(1) - const addFileTab = useMemoizedFn(() => { - // 新建临时文件 - console.log("ctrl_n") - const scratchFile: FileDetailInfo = { - name: `Untitle-${unTitleCountRef.current}.yak`, - code: "# input your yak code\nprintln(`Hello Yak World!`)", - icon: "_f_yak", - isActive: true, - openTimestamp: moment().unix(), - // 此处赋值 path 用于拖拽 分割布局等UI标识符操作 - path: `${uuidv4()}-Untitle-${unTitleCountRef.current}.yak`, - parent: null, - language: "yak", - isUnSave: true - } - unTitleCountRef.current += 1 - const {newAreaInfo, newActiveFile} = addAreaFileInfo(areaInfo, scratchFile, activeFile) + const addFileTab = useThrottleFn( + () => { + // 新建临时文件 + console.log("ctrl_n") + const scratchFile: FileDetailInfo = { + name: `Untitle-${unTitleCountRef.current}.yak`, + code: "# input your yak code\nprintln(`Hello Yak World!`)", + icon: "_f_yak", + isActive: true, + openTimestamp: moment().unix(), + // 此处赋值 path 用于拖拽 分割布局等UI标识符操作 + path: `${uuidv4()}-Untitle-${unTitleCountRef.current}.yak`, + parent: null, + language: "yak", + isUnSave: true + } + unTitleCountRef.current += 1 + const {newAreaInfo, newActiveFile} = addAreaFileInfo(areaInfo, scratchFile, activeFile) - setAreaInfo(newAreaInfo) - setActiveFile(newActiveFile) - }) + setAreaInfo(newAreaInfo) + setActiveFile(newActiveFile) + }, + {wait: 300} + ).run const [codePath, setCodePath] = useState("") // 默认保存路径 @@ -514,10 +517,10 @@ export const YakRunner: React.FC = (props) => { }) // 关闭文件 - const ctrl_w = useMemoizedFn(()=>{ - console.log("ctrl_w"); - if(activeFile){ - const newAreaInfo =removeAreaFileInfo(areaInfo,activeFile) + const ctrl_w = useMemoizedFn(() => { + console.log("ctrl_w") + if (activeFile) { + const newAreaInfo = removeAreaFileInfo(areaInfo, activeFile) setAreaInfo(newAreaInfo) } }) @@ -536,7 +539,7 @@ export const YakRunner: React.FC = (props) => { const handleKeyPress = (event) => { // 在这里处理全局键盘事件 - console.log("Key keydown:", event) + // console.log("Key keydown:", event) // 此处在使用key时发现字母竟区分大小写-故使用which替换 const {shiftKey, ctrlKey, altKey, metaKey, key, which} = event let activeKey: number[] = [] diff --git a/app/renderer/src/main/src/pages/YakRunner/utils.ts b/app/renderer/src/main/src/pages/YakRunner/utils.ts index 59264c0d15..eab51ede44 100644 --- a/app/renderer/src/main/src/pages/YakRunner/utils.ts +++ b/app/renderer/src/main/src/pages/YakRunner/utils.ts @@ -15,8 +15,8 @@ import {FileDetailInfo, OptionalFileDetailInfo} from "./RunnerTabs/RunnerTabsTyp import {v4 as uuidv4} from "uuid" import {getRemoteValue, setRemoteValue} from "@/utils/kv" import emiter from "@/utils/eventBus/eventBus" -import { setMapFileDetail } from "./FileTreeMap/FileMap" -import { setMapFolderDetail } from "./FileTreeMap/ChildMap" +import {setMapFileDetail} from "./FileTreeMap/FileMap" +import {setMapFolderDetail} from "./FileTreeMap/ChildMap" const {ipcRenderer} = window.require("electron") @@ -129,7 +129,11 @@ export const grpcFetchSaveFile: (path: string, code: string) => Promise Promise = (path, code,parentPath) => { +export const grpcFetchCreateFile: ( + path: string, + code?: string | null, + parentPath?: string | null +) => Promise = (path, code, parentPath) => { return new Promise(async (resolve, reject) => { const params: any = { Method: "PUT", @@ -156,7 +160,10 @@ export const grpcFetchCreateFile: (path: string, code?: string|null, parentPath? /** * @name 新建文件夹 */ -export const grpcFetchCreateFolder: (path: string, parentPath?: string | null) => Promise = (path,parentPath) => { +export const grpcFetchCreateFolder: (path: string, parentPath?: string | null) => Promise = ( + path, + parentPath +) => { return new Promise(async (resolve, reject) => { const params = { Method: "PUT", @@ -536,7 +543,7 @@ export const getOpenFileInfo = (): Promise<{path: string; name: string} | null> title: "请选择文件", properties: ["openFile"] }) - .then(async(data: {filePaths: string[]}) => { + .then(async (data: {filePaths: string[]}) => { const filesLength = data.filePaths.length if (filesLength === 1) { const path: string = data.filePaths[0].replace(/\\/g, "\\") @@ -556,6 +563,28 @@ export const getOpenFileInfo = (): Promise<{path: string; name: string} | null> }) } +/** + * @name 最大限制10M + */ +export const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024 + +/** + * @name 根据文件path获取其大小 + */ +export const getCodeSizeByPath = (path: string): Promise => { + return new Promise(async (resolve, reject) => { + ipcRenderer + .invoke("read-file-size", path) + .then((res) => { + resolve(res) + }) + .catch(() => { + failed("无法获取该文件大小,请检查后后重试!") + reject() + }) + }) +} + /** * @name 根据文件path获取其内容 */ @@ -631,7 +660,7 @@ export const setYakRunnerLastFolderPath = (newPath: string) => { /** * @name 获取上次文件夹打开路径 */ -export const getYakRunnerLastFolderPath = (): Promise => { +export const getYakRunnerLastFolderPath = (): Promise => { return new Promise(async (resolve, reject) => { getRemoteValue(YakRunnerLastFolderPath).then((data) => { try { @@ -651,7 +680,7 @@ export const getYakRunnerLastFolderPath = (): Promise => { /** * @name 路径拼接(兼容多系统) */ -export const getPathJoin = (path:string,file:string): Promise => { +export const getPathJoin = (path: string, file: string): Promise => { return new Promise(async (resolve, reject) => { ipcRenderer .invoke("pathJoin", { @@ -660,7 +689,8 @@ export const getPathJoin = (path:string,file:string): Promise => { }) .then((currentPath: string) => { resolve(currentPath) - }).catch(()=>{ + }) + .catch(() => { resolve("") }) }) @@ -669,7 +699,7 @@ export const getPathJoin = (path:string,file:string): Promise => { /** * @name 获取上一级的路径(兼容多系统) */ -export const getPathParent = (filePath:string): Promise => { +export const getPathParent = (filePath: string): Promise => { return new Promise(async (resolve, reject) => { ipcRenderer .invoke("pathParent", { @@ -677,7 +707,8 @@ export const getPathParent = (filePath:string): Promise => { }) .then((currentPath: string) => { resolve(currentPath) - }).catch(()=>{ + }) + .catch(() => { resolve("") }) }) @@ -686,7 +717,7 @@ export const getPathParent = (filePath:string): Promise => { /** * @name 获取路径上的(文件/文件夹)名(兼容多系统) */ -export const getNameByPath = (filePath:string): Promise => { +export const getNameByPath = (filePath: string): Promise => { return new Promise(async (resolve, reject) => { ipcRenderer .invoke("pathFileName", { @@ -694,7 +725,8 @@ export const getNameByPath = (filePath:string): Promise => { }) .then((currentName: string) => { resolve(currentName) - }).catch(()=>{ + }) + .catch(() => { resolve("") }) }) @@ -724,4 +756,4 @@ export const loadFolderDetail = (path) => { resolve(null) }) }) -} \ No newline at end of file +} diff --git a/app/renderer/src/main/src/routes/newRoute.tsx b/app/renderer/src/main/src/routes/newRoute.tsx index c8ffacdd29..c3a7cb57fb 100644 --- a/app/renderer/src/main/src/routes/newRoute.tsx +++ b/app/renderer/src/main/src/routes/newRoute.tsx @@ -417,8 +417,7 @@ export const RouteToPage: (props: PageItemProps) => ReactNode = (props) => { const {routeKey, yakScriptId, params} = props switch (routeKey) { case YakitRoute.NewHome: - return - // return + return case YakitRoute.HTTPHacker: return ( }> @@ -492,7 +491,8 @@ export const RouteToPage: (props: PageItemProps) => ReactNode = (props) => { case YakitRoute.DB_CVE: return case YakitRoute.YakScript: - return + return + // return case YakitRoute.PayloadManager: return case YakitRoute.AccountAdminPage: