@cryptiklemur/lattice 0.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.github/workflows/release.yml +4 -4
  2. package/.releaserc.json +2 -1
  3. package/client/src/components/auth/PassphrasePrompt.tsx +70 -70
  4. package/client/src/components/mesh/NodeBadge.tsx +24 -24
  5. package/client/src/components/mesh/PairingDialog.tsx +281 -281
  6. package/client/src/components/panels/FileBrowser.tsx +241 -241
  7. package/client/src/components/panels/StickyNotes.tsx +187 -187
  8. package/client/src/components/settings/Appearance.tsx +151 -151
  9. package/client/src/components/settings/MeshStatus.tsx +145 -145
  10. package/client/src/components/settings/SettingsView.tsx +57 -57
  11. package/client/src/components/setup/SetupWizard.tsx +750 -750
  12. package/client/src/components/ui/ErrorBoundary.tsx +56 -56
  13. package/client/src/router.tsx +391 -391
  14. package/client/vite.config.ts +20 -20
  15. package/package.json +1 -1
  16. package/server/src/handlers/chat.ts +194 -194
  17. package/server/src/handlers/settings.ts +109 -109
  18. package/themes/amoled.json +20 -20
  19. package/themes/ayu-light.json +9 -9
  20. package/themes/catppuccin-latte.json +9 -9
  21. package/themes/catppuccin-mocha.json +9 -9
  22. package/themes/clay-light.json +10 -10
  23. package/themes/clay.json +10 -10
  24. package/themes/dracula.json +9 -9
  25. package/themes/everforest-light.json +9 -9
  26. package/themes/everforest.json +9 -9
  27. package/themes/github-light.json +9 -9
  28. package/themes/gruvbox-dark.json +9 -9
  29. package/themes/gruvbox-light.json +9 -9
  30. package/themes/monokai.json +9 -9
  31. package/themes/nord-light.json +9 -9
  32. package/themes/nord.json +9 -9
  33. package/themes/one-dark.json +9 -9
  34. package/themes/one-light.json +9 -9
  35. package/themes/rose-pine-dawn.json +9 -9
  36. package/themes/rose-pine.json +9 -9
  37. package/themes/solarized-dark.json +9 -9
  38. package/themes/solarized-light.json +9 -9
  39. package/themes/tokyo-night-light.json +9 -9
  40. package/themes/tokyo-night.json +9 -9
  41. package/.serena/project.yml +0 -138
@@ -1,391 +1,391 @@
1
- import { createRouter, createRootRoute, createRoute, createMemoryHistory } from "@tanstack/react-router";
2
- import { Outlet } from "@tanstack/react-router";
3
- import { useState, useEffect, useRef } from "react";
4
- import { Sidebar } from "./components/sidebar/Sidebar";
5
- import { ChatView } from "./components/chat/ChatView";
6
- import { SetupWizard } from "./components/setup/SetupWizard";
7
- import { SettingsView } from "./components/settings/SettingsView";
8
- import { ProjectSettingsView } from "./components/project-settings/ProjectSettingsView";
9
- import { DashboardView } from "./components/dashboard/DashboardView";
10
- import { ProjectDashboardView } from "./components/dashboard/ProjectDashboardView";
11
- import { NodeSettingsModal } from "./components/sidebar/NodeSettingsModal";
12
- import { useSidebar } from "./hooks/useSidebar";
13
- import { useWebSocket } from "./hooks/useWebSocket";
14
- import { exitSettings, getSidebarStore, handlePopState, closeDrawer } from "./stores/sidebar";
15
-
16
- function LoadingScreen() {
17
- var ws = useWebSocket();
18
- var [dataReceived, setDataReceived] = useState(false);
19
- var [minTimeElapsed, setMinTimeElapsed] = useState(false);
20
- var canvasRef = useRef<HTMLCanvasElement>(null);
21
- var frameRef = useRef<number>(0);
22
-
23
- useEffect(function () {
24
- var timer = setTimeout(function () {
25
- setMinTimeElapsed(true);
26
- }, 600);
27
- return function () { clearTimeout(timer); };
28
- }, []);
29
-
30
- useEffect(function () {
31
- if (ws.status !== "connected") return;
32
- function handleProjects() {
33
- setDataReceived(true);
34
- }
35
- ws.subscribe("projects:list", handleProjects);
36
- return function () { ws.unsubscribe("projects:list", handleProjects); };
37
- }, [ws]);
38
-
39
- var ready = dataReceived && minTimeElapsed;
40
- var [visible, setVisible] = useState(true);
41
-
42
- useEffect(function () {
43
- if (!ready) return;
44
- var timer = setTimeout(function () {
45
- setVisible(false);
46
- }, 300);
47
- return function () { clearTimeout(timer); };
48
- }, [ready]);
49
-
50
- useEffect(function () {
51
- var canvas = canvasRef.current;
52
- if (!canvas) return;
53
- var ctx = canvas.getContext("2d");
54
- if (!ctx) return;
55
-
56
- var gridSize = 9;
57
- var spacing = 26;
58
- var dotRadius = 3.5;
59
- var padding = 20;
60
- var canvasSize = padding * 2 + (gridSize - 1) * spacing;
61
- var dpr = window.devicePixelRatio || 1;
62
-
63
- canvas.width = canvasSize * dpr;
64
- canvas.height = canvasSize * dpr;
65
- canvas.style.width = canvasSize + "px";
66
- canvas.style.height = canvasSize + "px";
67
- ctx.scale(dpr, dpr);
68
-
69
- var centerX = canvasSize / 2;
70
- var centerY = canvasSize / 2;
71
- var logoSquare = 14;
72
- var logoGap = 4;
73
- var logoHalf = (logoSquare * 2 + logoGap) / 2;
74
- var logoLeft = centerX - logoHalf;
75
- var logoTop = centerY - logoHalf;
76
- var logoRight = centerX + logoHalf;
77
- var logoBottom = centerY + logoHalf;
78
-
79
- var primary = "oklch(55% 0.25 280)";
80
-
81
- type Dot = { x: number; y: number; col: number; row: number; hidden: boolean; brightness: number };
82
- var dots: Dot[] = [];
83
- for (var row = 0; row < gridSize; row++) {
84
- for (var col = 0; col < gridSize; col++) {
85
- var x = padding + col * spacing;
86
- var y = padding + row * spacing;
87
- var hidden = x + dotRadius > logoLeft && x - dotRadius < logoRight &&
88
- y + dotRadius > logoTop && y - dotRadius < logoBottom;
89
- dots.push({ x: x, y: y, col: col, row: row, hidden: hidden, brightness: 0.08 });
90
- }
91
- }
92
-
93
- type Connection = {
94
- a: number; b: number;
95
- birth: number; duration: number;
96
- fadeIn: number; fadeOut: number;
97
- };
98
- var connections: Connection[] = [];
99
- var maxConnections = 10;
100
- var now = performance.now();
101
-
102
- function getNeighbors(idx: number): number[] {
103
- var d = dots[idx];
104
- var result: number[] = [];
105
- for (var dr = -1; dr <= 1; dr++) {
106
- for (var dc = -1; dc <= 1; dc++) {
107
- if (dr === 0 && dc === 0) continue;
108
- var nr = d.row + dr;
109
- var nc = d.col + dc;
110
- if (nr >= 0 && nr < gridSize && nc >= 0 && nc < gridSize) {
111
- var ni = nr * gridSize + nc;
112
- if (!dots[ni].hidden) result.push(ni);
113
- }
114
- }
115
- }
116
- return result;
117
- }
118
-
119
- function animate() {
120
- now = performance.now();
121
- ctx!.clearRect(0, 0, canvasSize, canvasSize);
122
-
123
- if (connections.length < maxConnections && Math.random() < 0.04) {
124
- var candidates: number[] = [];
125
- for (var i = 0; i < dots.length; i++) {
126
- if (!dots[i].hidden) candidates.push(i);
127
- }
128
- var attempts = 0;
129
- while (attempts < 5 && connections.length < maxConnections) {
130
- var ai = candidates[Math.floor(Math.random() * candidates.length)];
131
- var neighbors = getNeighbors(ai);
132
- if (neighbors.length > 0) {
133
- var bi = neighbors[Math.floor(Math.random() * neighbors.length)];
134
- var exists = false;
135
- for (var j = 0; j < connections.length; j++) {
136
- if ((connections[j].a === ai && connections[j].b === bi) ||
137
- (connections[j].a === bi && connections[j].b === ai)) {
138
- exists = true;
139
- break;
140
- }
141
- }
142
- if (!exists) {
143
- connections.push({
144
- a: ai, b: bi,
145
- birth: now,
146
- duration: 2000 + Math.random() * 3000,
147
- fadeIn: 400,
148
- fadeOut: 400,
149
- });
150
- break;
151
- }
152
- }
153
- attempts++;
154
- }
155
- }
156
-
157
- var connectedSet = new Set<number>();
158
- var keep: Connection[] = [];
159
- for (var ci = 0; ci < connections.length; ci++) {
160
- var c = connections[ci];
161
- var age = now - c.birth;
162
- if (age > c.duration) continue;
163
- keep.push(c);
164
-
165
- var alpha = 1.0;
166
- if (age < c.fadeIn) {
167
- alpha = age / c.fadeIn;
168
- } else if (age > c.duration - c.fadeOut) {
169
- alpha = (c.duration - age) / c.fadeOut;
170
- }
171
-
172
- connectedSet.add(c.a);
173
- connectedSet.add(c.b);
174
-
175
- var da = dots[c.a];
176
- var db = dots[c.b];
177
- ctx!.beginPath();
178
- ctx!.moveTo(da.x, da.y);
179
- ctx!.lineTo(db.x, db.y);
180
- ctx!.strokeStyle = primary;
181
- ctx!.globalAlpha = alpha * 0.35;
182
- ctx!.lineWidth = 1.5;
183
- ctx!.stroke();
184
- ctx!.globalAlpha = 1.0;
185
- }
186
- connections = keep;
187
-
188
- for (var di = 0; di < dots.length; di++) {
189
- var dot = dots[di];
190
- if (dot.hidden) continue;
191
- var targetBrightness = connectedSet.has(di) ? 0.7 : 0.08;
192
- dot.brightness += (targetBrightness - dot.brightness) * 0.08;
193
- ctx!.beginPath();
194
- ctx!.arc(dot.x, dot.y, dotRadius, 0, Math.PI * 2);
195
- ctx!.fillStyle = primary;
196
- ctx!.globalAlpha = dot.brightness;
197
- ctx!.fill();
198
- ctx!.globalAlpha = 1.0;
199
- }
200
-
201
- var pulse = 0.4 + 0.2 * Math.sin(now / 800);
202
- var squares = [
203
- [logoLeft, logoTop],
204
- [logoLeft + logoSquare + logoGap, logoTop],
205
- [logoLeft, logoTop + logoSquare + logoGap],
206
- [logoLeft + logoSquare + logoGap, logoTop + logoSquare + logoGap],
207
- ];
208
- for (var si = 0; si < squares.length; si++) {
209
- var sx = squares[si][0];
210
- var sy = squares[si][1];
211
- ctx!.save();
212
- ctx!.shadowColor = primary;
213
- ctx!.shadowBlur = 8 + pulse * 6;
214
- ctx!.fillStyle = primary;
215
- ctx!.globalAlpha = 0.85 + pulse * 0.15;
216
- ctx!.fillRect(sx, sy, logoSquare, logoSquare);
217
- ctx!.restore();
218
- }
219
-
220
- frameRef.current = requestAnimationFrame(animate);
221
- }
222
-
223
- frameRef.current = requestAnimationFrame(animate);
224
-
225
- return function () {
226
- cancelAnimationFrame(frameRef.current);
227
- };
228
- }, []);
229
-
230
- if (!visible) {
231
- return null;
232
- }
233
-
234
- var statusText = ws.status === "connecting" ? "Connecting..."
235
- : ws.status === "disconnected" ? "Reconnecting..."
236
- : "Loading projects...";
237
-
238
- return (
239
- <div
240
- className="fixed inset-0 z-[9999] flex flex-col items-center justify-center bg-base-100"
241
- style={{ opacity: ready ? 0 : 1, transition: "opacity 300ms ease-out", pointerEvents: ready ? "none" : "auto" }}
242
- >
243
- <div className="flex flex-col items-center gap-7">
244
- <canvas ref={canvasRef} />
245
- <div className="flex flex-col items-center gap-2">
246
- <span className="text-[16px] font-mono font-bold text-base-content tracking-tight">Lattice</span>
247
- <span className="text-[12px] text-base-content/40">{statusText}</span>
248
- </div>
249
- </div>
250
- </div>
251
- );
252
- }
253
-
254
- function RootLayout() {
255
- var [setupComplete, setSetupComplete] = useState(function () {
256
- return localStorage.getItem("lattice-setup-complete") === "1";
257
- });
258
-
259
- var sidebar = useSidebar();
260
-
261
- useEffect(function () {
262
- function handleKeyDown(e: KeyboardEvent) {
263
- if (e.key === "Escape") {
264
- var state = getSidebarStore().state;
265
- if (state.sidebarMode === "settings") {
266
- exitSettings();
267
- }
268
- }
269
- }
270
- document.addEventListener("keydown", handleKeyDown);
271
- window.addEventListener("popstate", handlePopState);
272
- return function () {
273
- document.removeEventListener("keydown", handleKeyDown);
274
- window.removeEventListener("popstate", handlePopState);
275
- };
276
- }, []);
277
-
278
- if (!setupComplete) {
279
- return (
280
- <SetupWizard onComplete={function () { setSetupComplete(true); }} />
281
- );
282
- }
283
-
284
- return (
285
- <div className="flex w-full h-full overflow-hidden bg-base-100">
286
- <LoadingScreen />
287
- <div className="drawer lg:drawer-open h-full w-full">
288
- <input
289
- id="sidebar-drawer"
290
- type="checkbox"
291
- className="drawer-toggle"
292
- checked={sidebar.drawerOpen}
293
- onChange={function () {}}
294
- />
295
-
296
- <div className="drawer-content flex flex-col h-full min-w-0 overflow-hidden">
297
- <Outlet />
298
- </div>
299
-
300
- <div className="drawer-side z-50 h-full">
301
- <label
302
- htmlFor="sidebar-drawer"
303
- aria-label="close sidebar"
304
- className="drawer-overlay"
305
- onClick={closeDrawer}
306
- />
307
- <div className="h-full w-[284px] flex flex-col overflow-hidden">
308
- <Sidebar onSessionSelect={closeDrawer} />
309
- </div>
310
- </div>
311
- </div>
312
- <NodeSettingsModal
313
- isOpen={sidebar.nodeSettingsOpen}
314
- onClose={sidebar.closeNodeSettings}
315
- />
316
- </div>
317
- );
318
- }
319
-
320
- function IndexPage() {
321
- var sidebar = useSidebar();
322
- if (sidebar.activeView.type === "dashboard") {
323
- return <DashboardView />;
324
- }
325
- if (sidebar.activeView.type === "settings") {
326
- return <SettingsView />;
327
- }
328
- if (sidebar.activeView.type === "project-settings") {
329
- return <ProjectSettingsView />;
330
- }
331
- if (sidebar.activeView.type === "project-dashboard") {
332
- return <ProjectDashboardView />;
333
- }
334
- return <ChatView />;
335
- }
336
-
337
- var rootRoute = createRootRoute({
338
- component: RootLayout,
339
- });
340
-
341
- var indexRoute = createRoute({
342
- getParentRoute: function () { return rootRoute; },
343
- path: "/",
344
- component: IndexPage,
345
- });
346
-
347
- var projectRoute = createRoute({
348
- getParentRoute: function () { return rootRoute; },
349
- path: "/$projectSlug",
350
- component: IndexPage,
351
- });
352
-
353
- var sessionRoute = createRoute({
354
- getParentRoute: function () { return rootRoute; },
355
- path: "/$projectSlug/$sessionId",
356
- component: IndexPage,
357
- });
358
-
359
- var settingsRoute = createRoute({
360
- getParentRoute: function () { return rootRoute; },
361
- path: "/settings/$section",
362
- component: IndexPage,
363
- });
364
-
365
- var settingsIndexRoute = createRoute({
366
- getParentRoute: function () { return rootRoute; },
367
- path: "/settings",
368
- component: IndexPage,
369
- });
370
-
371
- var projectSettingsRoute = createRoute({
372
- getParentRoute: function () { return rootRoute; },
373
- path: "/$projectSlug/settings/$section",
374
- component: IndexPage,
375
- });
376
-
377
- var projectSettingsIndexRoute = createRoute({
378
- getParentRoute: function () { return rootRoute; },
379
- path: "/$projectSlug/settings",
380
- component: IndexPage,
381
- });
382
-
383
- var routeTree = rootRoute.addChildren([indexRoute, settingsIndexRoute, settingsRoute, projectSettingsIndexRoute, projectSettingsRoute, projectRoute, sessionRoute]);
384
-
385
- export var router = createRouter({ routeTree });
386
-
387
- declare module "@tanstack/react-router" {
388
- interface Register {
389
- router: typeof router;
390
- }
391
- }
1
+ import { createRouter, createRootRoute, createRoute, createMemoryHistory } from "@tanstack/react-router";
2
+ import { Outlet } from "@tanstack/react-router";
3
+ import { useState, useEffect, useRef } from "react";
4
+ import { Sidebar } from "./components/sidebar/Sidebar";
5
+ import { ChatView } from "./components/chat/ChatView";
6
+ import { SetupWizard } from "./components/setup/SetupWizard";
7
+ import { SettingsView } from "./components/settings/SettingsView";
8
+ import { ProjectSettingsView } from "./components/project-settings/ProjectSettingsView";
9
+ import { DashboardView } from "./components/dashboard/DashboardView";
10
+ import { ProjectDashboardView } from "./components/dashboard/ProjectDashboardView";
11
+ import { NodeSettingsModal } from "./components/sidebar/NodeSettingsModal";
12
+ import { useSidebar } from "./hooks/useSidebar";
13
+ import { useWebSocket } from "./hooks/useWebSocket";
14
+ import { exitSettings, getSidebarStore, handlePopState, closeDrawer } from "./stores/sidebar";
15
+
16
+ function LoadingScreen() {
17
+ var ws = useWebSocket();
18
+ var [dataReceived, setDataReceived] = useState(false);
19
+ var [minTimeElapsed, setMinTimeElapsed] = useState(false);
20
+ var canvasRef = useRef<HTMLCanvasElement>(null);
21
+ var frameRef = useRef<number>(0);
22
+
23
+ useEffect(function () {
24
+ var timer = setTimeout(function () {
25
+ setMinTimeElapsed(true);
26
+ }, 600);
27
+ return function () { clearTimeout(timer); };
28
+ }, []);
29
+
30
+ useEffect(function () {
31
+ if (ws.status !== "connected") return;
32
+ function handleProjects() {
33
+ setDataReceived(true);
34
+ }
35
+ ws.subscribe("projects:list", handleProjects);
36
+ return function () { ws.unsubscribe("projects:list", handleProjects); };
37
+ }, [ws]);
38
+
39
+ var ready = dataReceived && minTimeElapsed;
40
+ var [visible, setVisible] = useState(true);
41
+
42
+ useEffect(function () {
43
+ if (!ready) return;
44
+ var timer = setTimeout(function () {
45
+ setVisible(false);
46
+ }, 300);
47
+ return function () { clearTimeout(timer); };
48
+ }, [ready]);
49
+
50
+ useEffect(function () {
51
+ var canvas = canvasRef.current;
52
+ if (!canvas) return;
53
+ var ctx = canvas.getContext("2d");
54
+ if (!ctx) return;
55
+
56
+ var gridSize = 9;
57
+ var spacing = 26;
58
+ var dotRadius = 3.5;
59
+ var padding = 20;
60
+ var canvasSize = padding * 2 + (gridSize - 1) * spacing;
61
+ var dpr = window.devicePixelRatio || 1;
62
+
63
+ canvas.width = canvasSize * dpr;
64
+ canvas.height = canvasSize * dpr;
65
+ canvas.style.width = canvasSize + "px";
66
+ canvas.style.height = canvasSize + "px";
67
+ ctx.scale(dpr, dpr);
68
+
69
+ var centerX = canvasSize / 2;
70
+ var centerY = canvasSize / 2;
71
+ var logoSquare = 14;
72
+ var logoGap = 4;
73
+ var logoHalf = (logoSquare * 2 + logoGap) / 2;
74
+ var logoLeft = centerX - logoHalf;
75
+ var logoTop = centerY - logoHalf;
76
+ var logoRight = centerX + logoHalf;
77
+ var logoBottom = centerY + logoHalf;
78
+
79
+ var primary = "oklch(55% 0.25 280)";
80
+
81
+ type Dot = { x: number; y: number; col: number; row: number; hidden: boolean; brightness: number };
82
+ var dots: Dot[] = [];
83
+ for (var row = 0; row < gridSize; row++) {
84
+ for (var col = 0; col < gridSize; col++) {
85
+ var x = padding + col * spacing;
86
+ var y = padding + row * spacing;
87
+ var hidden = x + dotRadius > logoLeft && x - dotRadius < logoRight &&
88
+ y + dotRadius > logoTop && y - dotRadius < logoBottom;
89
+ dots.push({ x: x, y: y, col: col, row: row, hidden: hidden, brightness: 0.08 });
90
+ }
91
+ }
92
+
93
+ type Connection = {
94
+ a: number; b: number;
95
+ birth: number; duration: number;
96
+ fadeIn: number; fadeOut: number;
97
+ };
98
+ var connections: Connection[] = [];
99
+ var maxConnections = 10;
100
+ var now = performance.now();
101
+
102
+ function getNeighbors(idx: number): number[] {
103
+ var d = dots[idx];
104
+ var result: number[] = [];
105
+ for (var dr = -1; dr <= 1; dr++) {
106
+ for (var dc = -1; dc <= 1; dc++) {
107
+ if (dr === 0 && dc === 0) continue;
108
+ var nr = d.row + dr;
109
+ var nc = d.col + dc;
110
+ if (nr >= 0 && nr < gridSize && nc >= 0 && nc < gridSize) {
111
+ var ni = nr * gridSize + nc;
112
+ if (!dots[ni].hidden) result.push(ni);
113
+ }
114
+ }
115
+ }
116
+ return result;
117
+ }
118
+
119
+ function animate() {
120
+ now = performance.now();
121
+ ctx!.clearRect(0, 0, canvasSize, canvasSize);
122
+
123
+ if (connections.length < maxConnections && Math.random() < 0.04) {
124
+ var candidates: number[] = [];
125
+ for (var i = 0; i < dots.length; i++) {
126
+ if (!dots[i].hidden) candidates.push(i);
127
+ }
128
+ var attempts = 0;
129
+ while (attempts < 5 && connections.length < maxConnections) {
130
+ var ai = candidates[Math.floor(Math.random() * candidates.length)];
131
+ var neighbors = getNeighbors(ai);
132
+ if (neighbors.length > 0) {
133
+ var bi = neighbors[Math.floor(Math.random() * neighbors.length)];
134
+ var exists = false;
135
+ for (var j = 0; j < connections.length; j++) {
136
+ if ((connections[j].a === ai && connections[j].b === bi) ||
137
+ (connections[j].a === bi && connections[j].b === ai)) {
138
+ exists = true;
139
+ break;
140
+ }
141
+ }
142
+ if (!exists) {
143
+ connections.push({
144
+ a: ai, b: bi,
145
+ birth: now,
146
+ duration: 2000 + Math.random() * 3000,
147
+ fadeIn: 400,
148
+ fadeOut: 400,
149
+ });
150
+ break;
151
+ }
152
+ }
153
+ attempts++;
154
+ }
155
+ }
156
+
157
+ var connectedSet = new Set<number>();
158
+ var keep: Connection[] = [];
159
+ for (var ci = 0; ci < connections.length; ci++) {
160
+ var c = connections[ci];
161
+ var age = now - c.birth;
162
+ if (age > c.duration) continue;
163
+ keep.push(c);
164
+
165
+ var alpha = 1.0;
166
+ if (age < c.fadeIn) {
167
+ alpha = age / c.fadeIn;
168
+ } else if (age > c.duration - c.fadeOut) {
169
+ alpha = (c.duration - age) / c.fadeOut;
170
+ }
171
+
172
+ connectedSet.add(c.a);
173
+ connectedSet.add(c.b);
174
+
175
+ var da = dots[c.a];
176
+ var db = dots[c.b];
177
+ ctx!.beginPath();
178
+ ctx!.moveTo(da.x, da.y);
179
+ ctx!.lineTo(db.x, db.y);
180
+ ctx!.strokeStyle = primary;
181
+ ctx!.globalAlpha = alpha * 0.35;
182
+ ctx!.lineWidth = 1.5;
183
+ ctx!.stroke();
184
+ ctx!.globalAlpha = 1.0;
185
+ }
186
+ connections = keep;
187
+
188
+ for (var di = 0; di < dots.length; di++) {
189
+ var dot = dots[di];
190
+ if (dot.hidden) continue;
191
+ var targetBrightness = connectedSet.has(di) ? 0.7 : 0.08;
192
+ dot.brightness += (targetBrightness - dot.brightness) * 0.08;
193
+ ctx!.beginPath();
194
+ ctx!.arc(dot.x, dot.y, dotRadius, 0, Math.PI * 2);
195
+ ctx!.fillStyle = primary;
196
+ ctx!.globalAlpha = dot.brightness;
197
+ ctx!.fill();
198
+ ctx!.globalAlpha = 1.0;
199
+ }
200
+
201
+ var pulse = 0.4 + 0.2 * Math.sin(now / 800);
202
+ var squares = [
203
+ [logoLeft, logoTop],
204
+ [logoLeft + logoSquare + logoGap, logoTop],
205
+ [logoLeft, logoTop + logoSquare + logoGap],
206
+ [logoLeft + logoSquare + logoGap, logoTop + logoSquare + logoGap],
207
+ ];
208
+ for (var si = 0; si < squares.length; si++) {
209
+ var sx = squares[si][0];
210
+ var sy = squares[si][1];
211
+ ctx!.save();
212
+ ctx!.shadowColor = primary;
213
+ ctx!.shadowBlur = 8 + pulse * 6;
214
+ ctx!.fillStyle = primary;
215
+ ctx!.globalAlpha = 0.85 + pulse * 0.15;
216
+ ctx!.fillRect(sx, sy, logoSquare, logoSquare);
217
+ ctx!.restore();
218
+ }
219
+
220
+ frameRef.current = requestAnimationFrame(animate);
221
+ }
222
+
223
+ frameRef.current = requestAnimationFrame(animate);
224
+
225
+ return function () {
226
+ cancelAnimationFrame(frameRef.current);
227
+ };
228
+ }, []);
229
+
230
+ if (!visible) {
231
+ return null;
232
+ }
233
+
234
+ var statusText = ws.status === "connecting" ? "Connecting..."
235
+ : ws.status === "disconnected" ? "Reconnecting..."
236
+ : "Loading projects...";
237
+
238
+ return (
239
+ <div
240
+ className="fixed inset-0 z-[9999] flex flex-col items-center justify-center bg-base-100"
241
+ style={{ opacity: ready ? 0 : 1, transition: "opacity 300ms ease-out", pointerEvents: ready ? "none" : "auto" }}
242
+ >
243
+ <div className="flex flex-col items-center gap-7">
244
+ <canvas ref={canvasRef} />
245
+ <div className="flex flex-col items-center gap-2">
246
+ <span className="text-[16px] font-mono font-bold text-base-content tracking-tight">Lattice</span>
247
+ <span className="text-[12px] text-base-content/40">{statusText}</span>
248
+ </div>
249
+ </div>
250
+ </div>
251
+ );
252
+ }
253
+
254
+ function RootLayout() {
255
+ var [setupComplete, setSetupComplete] = useState(function () {
256
+ return localStorage.getItem("lattice-setup-complete") === "1";
257
+ });
258
+
259
+ var sidebar = useSidebar();
260
+
261
+ useEffect(function () {
262
+ function handleKeyDown(e: KeyboardEvent) {
263
+ if (e.key === "Escape") {
264
+ var state = getSidebarStore().state;
265
+ if (state.sidebarMode === "settings") {
266
+ exitSettings();
267
+ }
268
+ }
269
+ }
270
+ document.addEventListener("keydown", handleKeyDown);
271
+ window.addEventListener("popstate", handlePopState);
272
+ return function () {
273
+ document.removeEventListener("keydown", handleKeyDown);
274
+ window.removeEventListener("popstate", handlePopState);
275
+ };
276
+ }, []);
277
+
278
+ if (!setupComplete) {
279
+ return (
280
+ <SetupWizard onComplete={function () { setSetupComplete(true); }} />
281
+ );
282
+ }
283
+
284
+ return (
285
+ <div className="flex w-full h-full overflow-hidden bg-base-100">
286
+ <LoadingScreen />
287
+ <div className="drawer lg:drawer-open h-full w-full">
288
+ <input
289
+ id="sidebar-drawer"
290
+ type="checkbox"
291
+ className="drawer-toggle"
292
+ checked={sidebar.drawerOpen}
293
+ onChange={function () {}}
294
+ />
295
+
296
+ <div className="drawer-content flex flex-col h-full min-w-0 overflow-hidden">
297
+ <Outlet />
298
+ </div>
299
+
300
+ <div className="drawer-side z-50 h-full">
301
+ <label
302
+ htmlFor="sidebar-drawer"
303
+ aria-label="close sidebar"
304
+ className="drawer-overlay"
305
+ onClick={closeDrawer}
306
+ />
307
+ <div className="h-full w-[284px] flex flex-col overflow-hidden">
308
+ <Sidebar onSessionSelect={closeDrawer} />
309
+ </div>
310
+ </div>
311
+ </div>
312
+ <NodeSettingsModal
313
+ isOpen={sidebar.nodeSettingsOpen}
314
+ onClose={sidebar.closeNodeSettings}
315
+ />
316
+ </div>
317
+ );
318
+ }
319
+
320
+ function IndexPage() {
321
+ var sidebar = useSidebar();
322
+ if (sidebar.activeView.type === "dashboard") {
323
+ return <DashboardView />;
324
+ }
325
+ if (sidebar.activeView.type === "settings") {
326
+ return <SettingsView />;
327
+ }
328
+ if (sidebar.activeView.type === "project-settings") {
329
+ return <ProjectSettingsView />;
330
+ }
331
+ if (sidebar.activeView.type === "project-dashboard") {
332
+ return <ProjectDashboardView />;
333
+ }
334
+ return <ChatView />;
335
+ }
336
+
337
+ var rootRoute = createRootRoute({
338
+ component: RootLayout,
339
+ });
340
+
341
+ var indexRoute = createRoute({
342
+ getParentRoute: function () { return rootRoute; },
343
+ path: "/",
344
+ component: IndexPage,
345
+ });
346
+
347
+ var projectRoute = createRoute({
348
+ getParentRoute: function () { return rootRoute; },
349
+ path: "/$projectSlug",
350
+ component: IndexPage,
351
+ });
352
+
353
+ var sessionRoute = createRoute({
354
+ getParentRoute: function () { return rootRoute; },
355
+ path: "/$projectSlug/$sessionId",
356
+ component: IndexPage,
357
+ });
358
+
359
+ var settingsRoute = createRoute({
360
+ getParentRoute: function () { return rootRoute; },
361
+ path: "/settings/$section",
362
+ component: IndexPage,
363
+ });
364
+
365
+ var settingsIndexRoute = createRoute({
366
+ getParentRoute: function () { return rootRoute; },
367
+ path: "/settings",
368
+ component: IndexPage,
369
+ });
370
+
371
+ var projectSettingsRoute = createRoute({
372
+ getParentRoute: function () { return rootRoute; },
373
+ path: "/$projectSlug/settings/$section",
374
+ component: IndexPage,
375
+ });
376
+
377
+ var projectSettingsIndexRoute = createRoute({
378
+ getParentRoute: function () { return rootRoute; },
379
+ path: "/$projectSlug/settings",
380
+ component: IndexPage,
381
+ });
382
+
383
+ var routeTree = rootRoute.addChildren([indexRoute, settingsIndexRoute, settingsRoute, projectSettingsIndexRoute, projectSettingsRoute, projectRoute, sessionRoute]);
384
+
385
+ export var router = createRouter({ routeTree });
386
+
387
+ declare module "@tanstack/react-router" {
388
+ interface Register {
389
+ router: typeof router;
390
+ }
391
+ }