Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
app/dist
30 changes: 18 additions & 12 deletions app/electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,29 @@ function createWindow() {
title: "",
frame: false,
titleBarOverlay: {
color: "#09090b", // Hintergrund
symbolColor: "#52525b" // Buttons
},
color: "#09090b", // Hintergrund
symbolColor: "#52525b" // Buttons
},
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
preload: path.join(__dirname, 'preload.js'),

contextIsolation: true,
sandbox: true,
nodeIntegration: false,
webviewTag: true,
partition: 'persist:main'
contextIsolation: true,
sandbox: true,
nodeIntegration: false,
webviewTag: true,
partition: 'persist:main'
},
});

mainWindow.setMenuBarVisibility(false);
mainWindow.setAutoHideMenuBar(true);
mainWindow.loadURL("http://localhost:3000")
const isDev = !app.isPackaged;

if (isDev) {
mainWindow.loadURL("http://localhost:3000");
} else {
mainWindow.loadFile(path.join(__dirname, "../frontend/out/index.html"));
}

}

Expand All @@ -47,7 +53,7 @@ ipcMain.on("window-minimize", () => {
}
})

ipcMain.on("window-close", () => {
ipcMain.on("window-close", () => {
if (mainWindow) {
mainWindow.close();
}
Expand All @@ -59,8 +65,8 @@ ipcMain.on("window-maximize", () => {
mainWindow.unmaximize();
} else {
mainWindow.maximize();
}
}
}
})

app.commandLine.appendSwitch("disable-features", "SitePerProcess,VizDisplayCompositor");
Expand Down
36 changes: 0 additions & 36 deletions app/frontend/README.md

This file was deleted.

Binary file modified app/frontend/app/favicon.ico
Binary file not shown.
4 changes: 2 additions & 2 deletions app/frontend/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ const geistMono = Geist_Mono({
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "QuickPad",
description: "QuickPad - The best way to code with your friends",
};

export default function RootLayout({
Expand Down
136 changes: 105 additions & 31 deletions app/frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,61 @@ import EditorPage from "@/components/editor";
import { useEffect, useRef, useState } from "react";
import Toolbar from "@/components/toolbar";
import { message } from "./types/message";
import FileBar from "@/components/filebar";

const USER_ID = Math.random().toString(36).substring(7);

export default function Home() {
const wsRef = useRef<WebSocket | null>(null);
const [sessionCode, setSessionCode] = useState<string>();
const [line, setLine] = useState<number>(1);
const [column, setColumn] = useState<number>(1);
const [data, setData] = useState<message>({ column: 1, line: 1, text: "" });
const [text, setText] = useState<string>("");
const [cursorPosition, setCursorPosition] = useState<number>(1);
const [cursorLine, setCursorLine] = useState<number>(1);
const [sharedCursorLine, setSharedCursorLine] = useState<number>(1);
const [sharedCursorPosition, setSharedCursorPosition] = useState<number>(1);
const [fileName, setFileName] = useState<string>("");

const [selectionStartLine, setSelectionStartLine] = useState<number>(1);
const [selectionStartColumn, setSelectionStartColumn] = useState<number>(1);
const [selectionEndLine, setSelectionEndLine] = useState<number>(1);
const [selectionEndColumn, setSelectionEndColumn] = useState<number>(1);

const [sharedSelectionStartLine, setSharedSelectionStartLine] = useState<number>(1);
const [sharedSelectionStartColumn, setSharedSelectionStartColumn] = useState<number>(1);
const [sharedSelectionEndLine, setSharedSelectionEndLine] = useState<number>(1);
const [sharedSelectionEndColumn, setSharedSelectionEndColumn] = useState<number>(1);

const [fileContent, setFileContent] = useState<string>("");
const [code, setCode] = useState<string>(`function greet(name) {
return "Hello " + name + "!";
}

console.log(greet("ChatGPT"));`);

const codeRef = useRef(code);
const sessionCodeRef = useRef(sessionCode);

useEffect(() => {
codeRef.current = code;
}, [code]);

useEffect(() => {
sessionCodeRef.current = sessionCode;
}, [sessionCode]);

useEffect(() => {
const connectToWebsocket = () => {
try {
const ws = new WebSocket("ws://localhost:8080");
wsRef.current = ws;

let pingInterval: NodeJS.Timeout;

ws.onopen = () => {
console.log("WebSocket connected");
pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ action: "ping" }));
}
}, 10000);
};

ws.onmessage = (event) => {
Expand All @@ -50,18 +74,41 @@ export default function Home() {
if (action === "cursor") {
setSharedCursorLine(data.cursorLine);
setSharedCursorPosition(data.cursorPosition);
} else if (action === "selection") {
setSharedSelectionStartLine(data.startLine);
setSharedSelectionStartColumn(data.startColumn);
setSharedSelectionEndLine(data.endLine);
setSharedSelectionEndColumn(data.endColumn);
} else if (action === "edit") {
const Message: message = {
column: data.column,
line: data.line,
endColumn: data.endColumn,
endLine: data.endLine,
text: data.text,
};
setData(Message);
} else if (action === "request_sync") {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify({
action: "sync_file",
code: sessionCodeRef.current,
userId: USER_ID,
targetId: data.targetId,
text: codeRef.current
}));
}
} else if (action === "sync_file") {
// Only sync if the message is targeted to us, or if it's a legacy broadcast (no targetId)
if (!data.targetId || data.targetId === USER_ID) {
setFileContent(data.text);
}
}
};

// try reconnecting after 3 seconds
ws.onclose = () => {
clearInterval(pingInterval);
setTimeout(connectToWebsocket, 3000);
};
} catch (error) {
Expand All @@ -77,72 +124,90 @@ export default function Home() {
};
}, []);

const createSession = () => {
const createSession = (code: string) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(
JSON.stringify({
action: "create",
userId: USER_ID,
code: "123456",
code: code,
})
);
setSessionCode("123456");
setSessionCode(code);
}
};

const joinSession = () => {
const joinSession = (code: string) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(
JSON.stringify({
action: "join",
userId: USER_ID,
code: code,
})
);
setSessionCode(code);
}
};

const handleEdit = (line: number, column: number, endLine: number, endColumn: number, text: string) => {
if (!sessionCodeRef.current) return;

code: "123456",
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(
JSON.stringify({
action: "edit",
code: sessionCodeRef.current,
line: line,
column: column,
endLine: endLine,
endColumn: endColumn,
userId: USER_ID,
text: text,
})
);
setSessionCode("123456");
}
};

useEffect(() => {
if (!text || !sessionCode) return;
if (!sessionCode) return;

const sendMessage = () => {
const sendCursorUpdate = () => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(
JSON.stringify({
action: "edit",
action: "cursor",
code: sessionCode,
line: line,
column: column,
userId: USER_ID,

text: text,
cursorLine: cursorLine,
cursorPosition: cursorPosition,
})
);
}
};
sendMessage();
}, [text, line, column]);
sendCursorUpdate();
}, [cursorLine, cursorPosition]);

useEffect(() => {
if (!sessionCode) return;

const sendCursorUpdate = () => {
const sendSelectionUpdate = () => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(
JSON.stringify({
action: "cursor",
action: "selection",
code: sessionCode,
userId: USER_ID,
cursorLine: cursorLine,
cursorPosition: cursorPosition,
startLine: selectionStartLine,
startColumn: selectionStartColumn,
endLine: selectionEndLine,
endColumn: selectionEndColumn,
})
);
}
};
sendCursorUpdate();
}, [cursorLine, cursorPosition]);
sendSelectionUpdate();
}, [selectionStartLine, selectionStartColumn, selectionEndLine, selectionEndColumn]);

useEffect(() => {
setCode(fileContent);
Expand All @@ -153,10 +218,10 @@ export default function Home() {
<Toolbar
createSession={createSession}
joinSession={joinSession}
setFileName={setFileName}
setFileContent={setFileContent}
fileContent={code}
sessionCode={sessionCode}
/>
<FileBar fileName={fileName} />

<div className="flex-1 flex flex-col overflow-hidden">
<div className={`${"h-full"} overflow-hidden`}>
Expand All @@ -165,12 +230,21 @@ export default function Home() {
sharedCursorPosition={sharedCursorPosition}
setCursorLine={setCursorLine}
setCursorPosition={setCursorPosition}
setLine={setLine}
setPosition={setColumn}

setSelectionStartLine={setSelectionStartLine}
setSelectionStartColumn={setSelectionStartColumn}
setSelectionEndLine={setSelectionEndLine}
setSelectionEndColumn={setSelectionEndColumn}

sharedSelectionStartLine={sharedSelectionStartLine}
sharedSelectionStartColumn={sharedSelectionStartColumn}
sharedSelectionEndLine={sharedSelectionEndLine}
sharedSelectionEndColumn={sharedSelectionEndColumn}

onEdit={handleEdit}
code={code}
setCode={setCode}
data={data}
setText={setText}
/>
</div>
</div>
Expand Down
10 changes: 6 additions & 4 deletions app/frontend/app/types/message.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export type message = {
column: number;
line: number;
text: string;
};
column: number;
line: number;
endColumn?: number;
endLine?: number;
text: string;
};
Loading