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