adding file logic
This commit is contained in:
parent
3b9aa900c8
commit
bce9d11b3b
4
.gitignore
vendored
4
.gitignore
vendored
@ -36,4 +36,6 @@ yarn-error.log*
|
|||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
wrangler.toml
|
wrangler.toml
|
||||||
|
|
||||||
|
backend/server/projects
|
48
backend/server/dist/index.js
vendored
48
backend/server/dist/index.js
vendored
@ -12,6 +12,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const fs_1 = __importDefault(require("fs"));
|
||||||
|
const path_1 = __importDefault(require("path"));
|
||||||
const express_1 = __importDefault(require("express"));
|
const express_1 = __importDefault(require("express"));
|
||||||
const dotenv_1 = __importDefault(require("dotenv"));
|
const dotenv_1 = __importDefault(require("dotenv"));
|
||||||
const http_1 = require("http");
|
const http_1 = require("http");
|
||||||
@ -30,6 +32,7 @@ const io = new socket_io_1.Server(httpServer, {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const terminals = {};
|
const terminals = {};
|
||||||
|
const dirName = path_1.default.join(__dirname, "..");
|
||||||
const handshakeSchema = zod_1.z.object({
|
const handshakeSchema = zod_1.z.object({
|
||||||
userId: zod_1.z.string(),
|
userId: zod_1.z.string(),
|
||||||
sandboxId: zod_1.z.string(),
|
sandboxId: zod_1.z.string(),
|
||||||
@ -67,12 +70,21 @@ io.use((socket, next) => __awaiter(void 0, void 0, void 0, function* () {
|
|||||||
io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
const data = socket.data;
|
const data = socket.data;
|
||||||
const sandboxFiles = yield (0, utils_1.getSandboxFiles)(data.id);
|
const sandboxFiles = yield (0, utils_1.getSandboxFiles)(data.id);
|
||||||
|
sandboxFiles.fileData.forEach((file) => {
|
||||||
|
const filePath = path_1.default.join(dirName, file.id);
|
||||||
|
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
||||||
|
fs_1.default.writeFile(filePath, file.data, function (err) {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
// console.log("Saved File:", file.id)
|
||||||
|
});
|
||||||
|
});
|
||||||
socket.emit("loaded", sandboxFiles.files);
|
socket.emit("loaded", sandboxFiles.files);
|
||||||
socket.on("getFile", (fileId, callback) => {
|
socket.on("getFile", (fileId, callback) => {
|
||||||
const file = sandboxFiles.fileData.find((f) => f.id === fileId);
|
const file = sandboxFiles.fileData.find((f) => f.id === fileId);
|
||||||
if (!file)
|
if (!file)
|
||||||
return;
|
return;
|
||||||
console.log("get file " + file.id + ": ", file.data.slice(0, 10) + "...");
|
// console.log("get file " + file.id + ": ", file.data.slice(0, 10) + "...")
|
||||||
callback(file.data);
|
callback(file.data);
|
||||||
});
|
});
|
||||||
// todo: send diffs + debounce for efficiency
|
// todo: send diffs + debounce for efficiency
|
||||||
@ -81,19 +93,47 @@ io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
|
|||||||
if (!file)
|
if (!file)
|
||||||
return;
|
return;
|
||||||
file.data = body;
|
file.data = body;
|
||||||
console.log("save file " + file.id + ": ", file.data);
|
// console.log("save file " + file.id + ": ", file.data)
|
||||||
|
fs_1.default.writeFile(path_1.default.join(dirName, file.id), body, function (err) {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
yield (0, utils_1.saveFile)(fileId, body);
|
yield (0, utils_1.saveFile)(fileId, body);
|
||||||
}));
|
}));
|
||||||
|
socket.on("createFile", (name) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
|
const id = `projects/${data.id}/${name}`;
|
||||||
|
console.log("create file", id, name);
|
||||||
|
fs_1.default.writeFile(path_1.default.join(dirName, id), "", function (err) {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
sandboxFiles.files.push({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
type: "file",
|
||||||
|
});
|
||||||
|
sandboxFiles.fileData.push({
|
||||||
|
id,
|
||||||
|
data: "",
|
||||||
|
});
|
||||||
|
yield (0, utils_1.createFile)(id);
|
||||||
|
}));
|
||||||
socket.on("renameFile", (fileId, newName) => __awaiter(void 0, void 0, void 0, function* () {
|
socket.on("renameFile", (fileId, newName) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
const file = sandboxFiles.fileData.find((f) => f.id === fileId);
|
const file = sandboxFiles.fileData.find((f) => f.id === fileId);
|
||||||
if (!file)
|
if (!file)
|
||||||
return;
|
return;
|
||||||
file.id = newName;
|
file.id = newName;
|
||||||
yield (0, utils_1.renameFile)(fileId, newName, file.data);
|
const parts = fileId.split("/");
|
||||||
|
const newFileId = parts.slice(0, parts.length - 1).join("/") + "/" + newName;
|
||||||
|
fs_1.default.rename(path_1.default.join(dirName, fileId), path_1.default.join(dirName, newFileId), function (err) {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
yield (0, utils_1.renameFile)(fileId, newFileId, file.data);
|
||||||
}));
|
}));
|
||||||
socket.on("createTerminal", ({ id }) => {
|
socket.on("createTerminal", ({ id }) => {
|
||||||
console.log("creating terminal (" + id + ")");
|
console.log("creating terminal (" + id + ")");
|
||||||
terminals[id] = new terminal_1.Pty(socket, id);
|
terminals[id] = new terminal_1.Pty(socket, id, `/projects/${data.id}`);
|
||||||
});
|
});
|
||||||
socket.on("terminalData", ({ id, data }) => {
|
socket.on("terminalData", ({ id, data }) => {
|
||||||
console.log(`Received data for terminal ${id}: ${data}`);
|
console.log(`Received data for terminal ${id}: ${data}`);
|
||||||
|
5
backend/server/dist/terminal.js
vendored
5
backend/server/dist/terminal.js
vendored
@ -7,13 +7,14 @@ exports.Pty = void 0;
|
|||||||
const node_pty_1 = require("node-pty");
|
const node_pty_1 = require("node-pty");
|
||||||
const os_1 = __importDefault(require("os"));
|
const os_1 = __importDefault(require("os"));
|
||||||
class Pty {
|
class Pty {
|
||||||
constructor(socket, id) {
|
constructor(socket, id, cwd) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.shell = os_1.default.platform() === "win32" ? "cmd.exe" : "bash";
|
this.shell = os_1.default.platform() === "win32" ? "cmd.exe" : "bash";
|
||||||
|
this.id = id;
|
||||||
this.ptyProcess = (0, node_pty_1.spawn)(this.shell, [], {
|
this.ptyProcess = (0, node_pty_1.spawn)(this.shell, [], {
|
||||||
name: "xterm",
|
name: "xterm",
|
||||||
cols: 100,
|
cols: 100,
|
||||||
cwd: `/temp`,
|
cwd: cwd,
|
||||||
// env: process.env as { [key: string]: string },
|
// env: process.env as { [key: string]: string },
|
||||||
});
|
});
|
||||||
this.ptyProcess.onData((data) => {
|
this.ptyProcess.onData((data) => {
|
||||||
|
17
backend/server/dist/utils.js
vendored
17
backend/server/dist/utils.js
vendored
@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.saveFile = exports.renameFile = exports.getSandboxFiles = void 0;
|
exports.saveFile = exports.renameFile = exports.createFile = exports.getSandboxFiles = void 0;
|
||||||
const getSandboxFiles = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
const getSandboxFiles = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
const sandboxRes = yield fetch(`https://storage.ishaan1013.workers.dev/api?sandboxId=${id}`);
|
const sandboxRes = yield fetch(`https://storage.ishaan1013.workers.dev/api?sandboxId=${id}`);
|
||||||
const sandboxData = yield sandboxRes.json();
|
const sandboxData = yield sandboxRes.json();
|
||||||
@ -77,9 +77,18 @@ const fetchFileContent = (fileId) => __awaiter(void 0, void 0, void 0, function*
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const renameFile = (fileId, newName, data) => __awaiter(void 0, void 0, void 0, function* () {
|
const createFile = (fileId) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
const parts = fileId.split("/");
|
const res = yield fetch(`https://storage.ishaan1013.workers.dev/api`, {
|
||||||
const newFileId = parts.slice(0, parts.length - 1).join("/") + "/" + newName;
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ fileId }),
|
||||||
|
});
|
||||||
|
return res.ok;
|
||||||
|
});
|
||||||
|
exports.createFile = createFile;
|
||||||
|
const renameFile = (fileId, newFileId, data) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
const res = yield fetch(`https://storage.ishaan1013.workers.dev/api/rename`, {
|
const res = yield fetch(`https://storage.ishaan1013.workers.dev/api/rename`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import fs from "fs"
|
||||||
|
import path from "path"
|
||||||
import express, { Express, NextFunction, Request, Response } from "express"
|
import express, { Express, NextFunction, Request, Response } from "express"
|
||||||
import dotenv from "dotenv"
|
import dotenv from "dotenv"
|
||||||
import { createServer } from "http"
|
import { createServer } from "http"
|
||||||
@ -5,7 +7,7 @@ import { Server } from "socket.io"
|
|||||||
|
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { User } from "./types"
|
import { User } from "./types"
|
||||||
import { getSandboxFiles, renameFile, saveFile } from "./utils"
|
import { createFile, getSandboxFiles, renameFile, saveFile } from "./utils"
|
||||||
import { Pty } from "./terminal"
|
import { Pty } from "./terminal"
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
@ -22,6 +24,8 @@ const io = new Server(httpServer, {
|
|||||||
|
|
||||||
const terminals: { [id: string]: Pty } = {}
|
const terminals: { [id: string]: Pty } = {}
|
||||||
|
|
||||||
|
const dirName = path.join(__dirname, "..")
|
||||||
|
|
||||||
const handshakeSchema = z.object({
|
const handshakeSchema = z.object({
|
||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
sandboxId: z.string(),
|
sandboxId: z.string(),
|
||||||
@ -72,6 +76,14 @@ io.on("connection", async (socket) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sandboxFiles = await getSandboxFiles(data.id)
|
const sandboxFiles = await getSandboxFiles(data.id)
|
||||||
|
sandboxFiles.fileData.forEach((file) => {
|
||||||
|
const filePath = path.join(dirName, file.id)
|
||||||
|
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
||||||
|
fs.writeFile(filePath, file.data, function (err) {
|
||||||
|
if (err) throw err
|
||||||
|
// console.log("Saved File:", file.id)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
socket.emit("loaded", sandboxFiles.files)
|
socket.emit("loaded", sandboxFiles.files)
|
||||||
|
|
||||||
@ -79,7 +91,7 @@ io.on("connection", async (socket) => {
|
|||||||
const file = sandboxFiles.fileData.find((f) => f.id === fileId)
|
const file = sandboxFiles.fileData.find((f) => f.id === fileId)
|
||||||
if (!file) return
|
if (!file) return
|
||||||
|
|
||||||
console.log("get file " + file.id + ": ", file.data.slice(0, 10) + "...")
|
// console.log("get file " + file.id + ": ", file.data.slice(0, 10) + "...")
|
||||||
callback(file.data)
|
callback(file.data)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -88,22 +100,57 @@ io.on("connection", async (socket) => {
|
|||||||
const file = sandboxFiles.fileData.find((f) => f.id === fileId)
|
const file = sandboxFiles.fileData.find((f) => f.id === fileId)
|
||||||
if (!file) return
|
if (!file) return
|
||||||
file.data = body
|
file.data = body
|
||||||
|
// console.log("save file " + file.id + ": ", file.data)
|
||||||
|
|
||||||
console.log("save file " + file.id + ": ", file.data)
|
fs.writeFile(path.join(dirName, file.id), body, function (err) {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
await saveFile(fileId, body)
|
await saveFile(fileId, body)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.on("createFile", async (name: string) => {
|
||||||
|
const id = `projects/${data.id}/${name}`
|
||||||
|
console.log("create file", id, name)
|
||||||
|
|
||||||
|
fs.writeFile(path.join(dirName, id), "", function (err) {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
|
|
||||||
|
sandboxFiles.files.push({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
type: "file",
|
||||||
|
})
|
||||||
|
|
||||||
|
sandboxFiles.fileData.push({
|
||||||
|
id,
|
||||||
|
data: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
await createFile(id)
|
||||||
|
})
|
||||||
|
|
||||||
socket.on("renameFile", async (fileId: string, newName: string) => {
|
socket.on("renameFile", async (fileId: string, newName: string) => {
|
||||||
const file = sandboxFiles.fileData.find((f) => f.id === fileId)
|
const file = sandboxFiles.fileData.find((f) => f.id === fileId)
|
||||||
if (!file) return
|
if (!file) return
|
||||||
file.id = newName
|
file.id = newName
|
||||||
|
|
||||||
await renameFile(fileId, newName, file.data)
|
const parts = fileId.split("/")
|
||||||
|
const newFileId = parts.slice(0, parts.length - 1).join("/") + "/" + newName
|
||||||
|
|
||||||
|
fs.rename(
|
||||||
|
path.join(dirName, fileId),
|
||||||
|
path.join(dirName, newFileId),
|
||||||
|
function (err) {
|
||||||
|
if (err) throw err
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await renameFile(fileId, newFileId, file.data)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("createTerminal", ({ id }: { id: string }) => {
|
socket.on("createTerminal", ({ id }: { id: string }) => {
|
||||||
console.log("creating terminal (" + id + ")")
|
console.log("creating terminal (" + id + ")")
|
||||||
terminals[id] = new Pty(socket, id)
|
terminals[id] = new Pty(socket, id, `/projects/${data.id}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("terminalData", ({ id, data }: { id: string; data: string }) => {
|
socket.on("terminalData", ({ id, data }: { id: string; data: string }) => {
|
||||||
|
@ -6,15 +6,17 @@ export class Pty {
|
|||||||
socket: Socket
|
socket: Socket
|
||||||
ptyProcess: IPty
|
ptyProcess: IPty
|
||||||
shell: string
|
shell: string
|
||||||
|
id: string
|
||||||
|
|
||||||
constructor(socket: Socket, id: string) {
|
constructor(socket: Socket, id: string, cwd: string) {
|
||||||
this.socket = socket
|
this.socket = socket
|
||||||
this.shell = os.platform() === "win32" ? "cmd.exe" : "bash"
|
this.shell = os.platform() === "win32" ? "cmd.exe" : "bash"
|
||||||
|
this.id = id
|
||||||
|
|
||||||
this.ptyProcess = spawn(this.shell, [], {
|
this.ptyProcess = spawn(this.shell, [], {
|
||||||
name: "xterm",
|
name: "xterm",
|
||||||
cols: 100,
|
cols: 100,
|
||||||
cwd: `/temp`,
|
cwd: cwd,
|
||||||
// env: process.env as { [key: string]: string },
|
// env: process.env as { [key: string]: string },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -87,14 +87,22 @@ const fetchFileContent = async (fileId: string): Promise<string> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createFile = async (fileId: string) => {
|
||||||
|
const res = await fetch(`https://storage.ishaan1013.workers.dev/api`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ fileId }),
|
||||||
|
})
|
||||||
|
return res.ok
|
||||||
|
}
|
||||||
|
|
||||||
export const renameFile = async (
|
export const renameFile = async (
|
||||||
fileId: string,
|
fileId: string,
|
||||||
newName: string,
|
newFileId: string,
|
||||||
data: string
|
data: string
|
||||||
) => {
|
) => {
|
||||||
const parts = fileId.split("/")
|
|
||||||
const newFileId = parts.slice(0, parts.length - 1).join("/") + "/" + newName
|
|
||||||
|
|
||||||
const res = await fetch(`https://storage.ishaan1013.workers.dev/api/rename`, {
|
const res = await fetch(`https://storage.ishaan1013.workers.dev/api/rename`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -41,7 +41,16 @@ export default {
|
|||||||
});
|
});
|
||||||
} else return invalidRequest;
|
} else return invalidRequest;
|
||||||
} else if (method === 'POST') {
|
} else if (method === 'POST') {
|
||||||
return new Response('Hello, world!');
|
const createSchema = z.object({
|
||||||
|
fileId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const body = await request.json();
|
||||||
|
const { fileId } = createSchema.parse(body);
|
||||||
|
|
||||||
|
await env.R2.put(fileId, '');
|
||||||
|
|
||||||
|
return success;
|
||||||
} else return methodNotAllowed;
|
} else return methodNotAllowed;
|
||||||
} else if (path === '/api/rename' && method === 'POST') {
|
} else if (path === '/api/rename' && method === 'POST') {
|
||||||
const renameSchema = z.object({
|
const renameSchema = z.object({
|
||||||
|
@ -23,7 +23,7 @@ import { useClerk } from "@clerk/nextjs"
|
|||||||
import { TFile, TFileData, TFolder, TTab } from "./sidebar/types"
|
import { TFile, TFileData, TFolder, TTab } from "./sidebar/types"
|
||||||
|
|
||||||
import { io } from "socket.io-client"
|
import { io } from "socket.io-client"
|
||||||
import { processFileType } from "@/lib/utils"
|
import { processFileType, validateName } from "@/lib/utils"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
import EditorTerminal from "./terminal"
|
import EditorTerminal from "./terminal"
|
||||||
|
|
||||||
@ -158,21 +158,7 @@ export default function CodeEditor({
|
|||||||
oldName: string,
|
oldName: string,
|
||||||
type: "file" | "folder"
|
type: "file" | "folder"
|
||||||
) => {
|
) => {
|
||||||
// Validation
|
if (!validateName(newName, oldName, type)) return false
|
||||||
if (newName === oldName) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
newName.includes("/") ||
|
|
||||||
newName.includes("\\") ||
|
|
||||||
newName.includes(" ") ||
|
|
||||||
(type === "file" && !newName.includes(".")) ||
|
|
||||||
(type === "folder" && newName.includes("."))
|
|
||||||
) {
|
|
||||||
toast.error("Invalid file name.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
socket.emit("renameFile", id, newName)
|
socket.emit("renameFile", id, newName)
|
||||||
@ -189,6 +175,19 @@ export default function CodeEditor({
|
|||||||
files={files}
|
files={files}
|
||||||
selectFile={selectFile}
|
selectFile={selectFile}
|
||||||
handleRename={handleRename}
|
handleRename={handleRename}
|
||||||
|
socket={socket}
|
||||||
|
addNew={(name, type) => {
|
||||||
|
if (type === "file") {
|
||||||
|
console.log("adding file")
|
||||||
|
setFiles((prev) => [
|
||||||
|
...prev,
|
||||||
|
{ id: `projects/${sandboxId}/${name}`, name, type: "file" },
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
console.log("adding folder")
|
||||||
|
// setFiles(prev => [...prev, { id, name, type: "folder", children: [] }])
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<ResizablePanelGroup direction="horizontal">
|
<ResizablePanelGroup direction="horizontal">
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
|
@ -6,11 +6,14 @@ import SidebarFolder from "./folder"
|
|||||||
import { TFile, TFolder, TTab } from "./types"
|
import { TFile, TFolder, TTab } from "./types"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import New from "./new"
|
import New from "./new"
|
||||||
|
import { Socket } from "socket.io-client"
|
||||||
|
|
||||||
export default function Sidebar({
|
export default function Sidebar({
|
||||||
files,
|
files,
|
||||||
selectFile,
|
selectFile,
|
||||||
handleRename,
|
handleRename,
|
||||||
|
socket,
|
||||||
|
addNew,
|
||||||
}: {
|
}: {
|
||||||
files: (TFile | TFolder)[]
|
files: (TFile | TFolder)[]
|
||||||
selectFile: (tab: TTab) => void
|
selectFile: (tab: TTab) => void
|
||||||
@ -20,6 +23,8 @@ export default function Sidebar({
|
|||||||
oldName: string,
|
oldName: string,
|
||||||
type: "file" | "folder"
|
type: "file" | "folder"
|
||||||
) => boolean
|
) => boolean
|
||||||
|
socket: Socket
|
||||||
|
addNew: (name: string, type: "file" | "folder") => void
|
||||||
}) {
|
}) {
|
||||||
const [creatingNew, setCreatingNew] = useState<"file" | "folder" | null>(null)
|
const [creatingNew, setCreatingNew] = useState<"file" | "folder" | null>(null)
|
||||||
|
|
||||||
@ -73,8 +78,13 @@ export default function Sidebar({
|
|||||||
)}
|
)}
|
||||||
{creatingNew !== null ? (
|
{creatingNew !== null ? (
|
||||||
<New
|
<New
|
||||||
|
socket={socket}
|
||||||
type={creatingNew}
|
type={creatingNew}
|
||||||
stopEditing={() => setCreatingNew(null)}
|
stopEditing={() => {
|
||||||
|
console.log("stopped editing")
|
||||||
|
setCreatingNew(null)
|
||||||
|
}}
|
||||||
|
addNew={addNew}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
@ -1,19 +1,33 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { validateName } from "@/lib/utils"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useEffect, useRef } from "react"
|
import { useEffect, useRef } from "react"
|
||||||
|
import { Socket } from "socket.io-client"
|
||||||
|
|
||||||
export default function New({
|
export default function New({
|
||||||
|
socket,
|
||||||
type,
|
type,
|
||||||
stopEditing,
|
stopEditing,
|
||||||
|
addNew,
|
||||||
}: {
|
}: {
|
||||||
|
socket: Socket
|
||||||
type: "file" | "folder"
|
type: "file" | "folder"
|
||||||
stopEditing: () => void
|
stopEditing: () => void
|
||||||
|
addNew: (name: string, type: "file" | "folder") => void
|
||||||
}) {
|
}) {
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
const createFile = () => {
|
const createNew = () => {
|
||||||
console.log("Create File")
|
const name = inputRef.current?.value
|
||||||
|
// console.log("Create:", name, type)
|
||||||
|
|
||||||
|
if (name && validateName(name, "", type)) {
|
||||||
|
if (type === "file") {
|
||||||
|
socket.emit("createFile", name)
|
||||||
|
}
|
||||||
|
addNew(name, type)
|
||||||
|
}
|
||||||
stopEditing()
|
stopEditing()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,13 +51,13 @@ export default function New({
|
|||||||
<form
|
<form
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
createFile()
|
createNew()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className={`bg-transparent transition-all focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-ring rounded-sm w-full`}
|
className={`bg-transparent transition-all focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-ring rounded-sm w-full`}
|
||||||
onBlur={() => createFile()}
|
onBlur={() => createNew()}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { type ClassValue, clsx } from "clsx"
|
import { type ClassValue, clsx } from "clsx"
|
||||||
|
import { toast } from "sonner"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
@ -18,3 +19,25 @@ export function processFileType(file: string) {
|
|||||||
export function decodeTerminalResponse(buffer: Buffer): string {
|
export function decodeTerminalResponse(buffer: Buffer): string {
|
||||||
return buffer.toString("utf-8")
|
return buffer.toString("utf-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function validateName(
|
||||||
|
newName: string,
|
||||||
|
oldName: string,
|
||||||
|
type: "file" | "folder"
|
||||||
|
) {
|
||||||
|
if (newName === oldName || newName.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
newName.includes("/") ||
|
||||||
|
newName.includes("\\") ||
|
||||||
|
newName.includes(" ") ||
|
||||||
|
(type === "file" && !newName.includes(".")) ||
|
||||||
|
(type === "folder" && newName.includes("."))
|
||||||
|
) {
|
||||||
|
toast.error("Invalid file name.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user