Skip to content

Commit 114831b

Browse files
committed
new devtools ui
1 parent b528b56 commit 114831b

File tree

40 files changed

+497
-396
lines changed

40 files changed

+497
-396
lines changed

packages/devtools/src/app/main.ts

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
import { app, BrowserWindow, clipboard, Data, ipcMain, nativeImage } from "electron";
1+
import { app, BrowserWindow } from "electron";
22
import path from "node:path";
33
import started from "electron-squirrel-startup";
4-
import Store from "electron-store";
54

65
import { startServer } from "../server";
6+
import { setupWindowControls } from "./src/window-controls";
7+
import { persistentStore } from "./src/persistent-store";
8+
import { copyToClipboard } from "./src/clipboard";
79

810
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
911
if (started) {
1012
app.quit();
1113
}
1214

13-
const store = new Store();
1415
const createWindow = () => {
1516
// Create the browser window.
1617
const mainWindow = new BrowserWindow({
@@ -43,29 +44,9 @@ const createWindow = () => {
4344
app.on("ready", createWindow);
4445

4546
app.whenReady().then(() => {
46-
ipcMain.on("electron-store-get", async (event, val) => {
47-
// eslint-disable-next-line no-param-reassign
48-
event.returnValue = store.get(val);
49-
});
50-
ipcMain.on("electron-store-set", async (_, key, val) => {
51-
store.set(key, val);
52-
});
53-
54-
ipcMain.on("electron-store-delete", async (_, key) => {
55-
try {
56-
store.delete(key);
57-
} catch (error) {
58-
console.error("🚀 ~ ipcMain.on ~ error:", error);
59-
}
60-
});
61-
62-
ipcMain.on("clipboard", async (_, val: Data & { img?: string }) => {
63-
if (val.img) {
64-
clipboard.writeImage(nativeImage.createFromBuffer(Buffer.from(val.img, "base64")));
65-
} else {
66-
clipboard.write(val);
67-
}
68-
});
47+
copyToClipboard();
48+
persistentStore();
49+
setupWindowControls();
6950
});
7051

7152
// Quit when all windows are closed, except on macOS. There, it's common
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Data, clipboard, ipcMain, nativeImage } from "electron";
2+
3+
export const copyToClipboard = () => {
4+
ipcMain.on("clipboard", async (_, val: Data & { img?: string }) => {
5+
if (val.img) {
6+
clipboard.writeImage(nativeImage.createFromBuffer(Buffer.from(val.img, "base64")));
7+
} else {
8+
clipboard.write(val);
9+
}
10+
});
11+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ipcMain } from "electron";
2+
import Store from "electron-store";
3+
4+
const store = new Store();
5+
6+
export const persistentStore = () => {
7+
ipcMain.on("electron-store-get", async (event, val) => {
8+
// eslint-disable-next-line no-param-reassign
9+
event.returnValue = store.get(val);
10+
});
11+
ipcMain.on("electron-store-set", async (_, key, val) => {
12+
store.set(key, val);
13+
});
14+
15+
ipcMain.on("electron-store-delete", async (_, key) => {
16+
try {
17+
store.delete(key);
18+
} catch (error) {
19+
console.error("🚀 ~ ipcMain.on ~ error:", error);
20+
}
21+
});
22+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { BrowserWindow, ipcMain } from "electron";
2+
3+
export function setupWindowControls() {
4+
ipcMain.on("window-minimize", (event) => {
5+
const window = BrowserWindow.fromWebContents(event.sender);
6+
if (window) window.minimize();
7+
});
8+
9+
ipcMain.on("window-maximize", (event) => {
10+
const window = BrowserWindow.fromWebContents(event.sender);
11+
if (!window) return;
12+
13+
if (window.isMaximized()) {
14+
window.unmaximize();
15+
} else {
16+
window.maximize();
17+
}
18+
});
19+
20+
ipcMain.on("window-close", (event) => {
21+
const window = BrowserWindow.fromWebContents(event.sender);
22+
if (window) window.close();
23+
});
24+
}
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import { Layout } from "./components/layout/layout";
21
import { Application } from "./routing/router";
32
import { ProjectsProvider } from "./context/projects/projects";
43

54
export function App() {
65
return (
7-
<Layout>
8-
<ProjectsProvider>
9-
<Application />
10-
</ProjectsProvider>
11-
</Layout>
6+
<ProjectsProvider>
7+
<Application />
8+
</ProjectsProvider>
129
);
1310
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Outlet } from "@reins/router";
2+
3+
import { Navbar } from "./navbar/navbar";
4+
import { useIsFocused } from "frontend/hooks/use-is-focused";
5+
import { cn } from "frontend/lib/utils";
6+
7+
export const App = () => {
8+
const isFocused = useIsFocused();
9+
10+
return (
11+
<div
12+
className={cn("h-full w-full flex flex-col relative", {
13+
"brightness-90 opacity-90": !isFocused,
14+
})}
15+
>
16+
<Navbar />
17+
<div className="flex-1 flex flex-col">
18+
<Outlet />
19+
</div>
20+
</div>
21+
);
22+
};
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import React from "react";
2+
import { Home, Package, Maximize2, X, Minus } from "lucide-react";
3+
4+
import {
5+
Breadcrumb,
6+
BreadcrumbItem,
7+
BreadcrumbLink,
8+
BreadcrumbList,
9+
BreadcrumbPage,
10+
BreadcrumbSeparator,
11+
} from "frontend/components/ui/breadcrumb";
12+
// import { Button } from "frontend/components/ui/button";
13+
// import { NavUser } from "./nav-user";
14+
import { Link, useBreadcrumbs } from "frontend/routing/router";
15+
import { useIsFocused } from "frontend/hooks/use-is-focused";
16+
17+
const dragStyle = {
18+
"-webkit-user-select": "none",
19+
"-webkit-app-region": "drag",
20+
} as React.CSSProperties;
21+
22+
const noDragStyle = {
23+
"-webkit-user-select": "none",
24+
"-webkit-app-region": "no-drag",
25+
} as React.CSSProperties;
26+
27+
export const Navbar = () => {
28+
const isFocused = useIsFocused();
29+
const breadcrumbs = useBreadcrumbs();
30+
31+
const handleClose = () => {
32+
window.electron.ipcRenderer.send("window-close");
33+
};
34+
35+
const handleMinimize = () => {
36+
window.electron.ipcRenderer.send("window-minimize");
37+
};
38+
39+
const handleMaximize = () => {
40+
window.electron.ipcRenderer.send("window-maximize");
41+
};
42+
43+
console.log(breadcrumbs);
44+
45+
return (
46+
<div className="flex gap-1 pt-2 px-3 justify-between" style={dragStyle}>
47+
<div className="flex gap-1 items-center">
48+
<div className="window-controls w-22">
49+
<div className="flex gap-2 items-center">
50+
<button
51+
id="close-btn"
52+
className={`w-3 h-3 rounded-full transition-colors flex items-center justify-center group ${
53+
isFocused ? "bg-red-500 hover:bg-red-600" : "bg-gray-700"
54+
}`}
55+
aria-label="Close"
56+
type="button"
57+
style={noDragStyle}
58+
onClick={handleClose}
59+
>
60+
<X className="w-2.5 h-2.5 opacity-0 group-hover:opacity-100 transition-opacity" stroke="#4A0002" />
61+
</button>
62+
<button
63+
id="min-btn"
64+
className={`w-3 h-3 rounded-full transition-colors flex items-center justify-center group ${
65+
isFocused ? "bg-yellow-500 hover:bg-yellow-600" : "bg-gray-700"
66+
}`}
67+
aria-label="Minimize"
68+
type="button"
69+
style={noDragStyle}
70+
onClick={handleMinimize}
71+
>
72+
<Minus className="w-2.5 h-2.5 opacity-0 group-hover:opacity-100 transition-opacity" stroke="#4A3802" />
73+
</button>
74+
<button
75+
id="max-btn"
76+
className={`w-3 h-3 rounded-full transition-colors flex items-center justify-center group ${
77+
isFocused ? "bg-green-500 hover:bg-green-600" : "bg-gray-700"
78+
}`}
79+
aria-label="Maximize"
80+
type="button"
81+
style={noDragStyle}
82+
onClick={handleMaximize}
83+
>
84+
<Maximize2 className="w-2 h-2 opacity-0 group-hover:opacity-100 transition-opacity" stroke="#0A4102" />
85+
</button>
86+
</div>
87+
</div>
88+
<div className="flex gap-1 items-center">
89+
<Breadcrumb>
90+
<BreadcrumbList>
91+
<BreadcrumbItem className="hidden md:block">
92+
<BreadcrumbLink className="flex gap-1 items-center" style={noDragStyle} asChild>
93+
<Link to="dashboard">
94+
<Home className="w-4 h-4" /> Home
95+
</Link>
96+
</BreadcrumbLink>
97+
</BreadcrumbItem>
98+
{breadcrumbs.map((data, index) => {
99+
const { name, path } = data;
100+
const isLast = index === breadcrumbs.length - 1;
101+
102+
const isProject = path.includes("project") && path.split("/").length === 3;
103+
104+
if (!isLast) {
105+
return (
106+
<>
107+
<BreadcrumbSeparator className="hidden md:block" />
108+
<BreadcrumbItem>
109+
<BreadcrumbLink
110+
href={path}
111+
className="flex gap-1 items-center capitalize"
112+
style={noDragStyle}
113+
asChild
114+
>
115+
<Link href={path}>
116+
{isProject ? <Package className="w-4 h-4" /> : null}
117+
{name}
118+
</Link>
119+
</BreadcrumbLink>
120+
</BreadcrumbItem>
121+
</>
122+
);
123+
}
124+
125+
return (
126+
<>
127+
<BreadcrumbSeparator className="hidden md:block" />
128+
<BreadcrumbItem>
129+
<BreadcrumbPage className="capitalize">
130+
{isProject ? <Package className="w-4 h-4" /> : null}
131+
{name}
132+
</BreadcrumbPage>
133+
</BreadcrumbItem>
134+
</>
135+
);
136+
})}
137+
</BreadcrumbList>
138+
</Breadcrumb>
139+
</div>
140+
</div>
141+
<div className="flex gap-3 items-center text-sm text-gray-400" style={noDragStyle}>
142+
{/* <Button size="sm">
143+
<Rocket className="w-3.5 h-3.5" />
144+
Upgrade
145+
</Button>
146+
<Button variant="ghost" size="icon">
147+
<RefreshCcw className="w-3.5 h-3.5" />
148+
</Button>
149+
<Button variant="ghost" size="icon">
150+
<Settings className="w-3.5 h-3.5" />
151+
</Button>
152+
<Button variant="ghost" size="icon">
153+
<Bell className="w-3.5 h-3.5" />
154+
</Button>
155+
<NavUser /> */}
156+
</div>
157+
</div>
158+
);
159+
};

packages/devtools/src/frontend/components/layout/layout.tsx

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)