@cryptiklemur/lattice 1.11.4 → 1.11.6
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from "react";
|
|
1
|
+
import { useEffect, useRef, useState, useMemo } from "react";
|
|
2
2
|
import type { SessionSummary, SessionListMessage, SessionCreatedMessage } from "@lattice/shared";
|
|
3
3
|
import type { ServerMessage } from "@lattice/shared";
|
|
4
4
|
import { useWebSocket } from "../../hooks/useWebSocket";
|
|
@@ -251,6 +251,15 @@ export function SessionList(props: SessionListProps) {
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
var grouped = useMemo(function () {
|
|
255
|
+
var displayed = props.filter
|
|
256
|
+
? sessions.filter(function (s) {
|
|
257
|
+
return s.title.toLowerCase().includes(props.filter!.toLowerCase());
|
|
258
|
+
})
|
|
259
|
+
: sessions;
|
|
260
|
+
return groupByTime(displayed);
|
|
261
|
+
}, [sessions, props.filter]);
|
|
262
|
+
|
|
254
263
|
if (!props.projectSlug) {
|
|
255
264
|
return (
|
|
256
265
|
<div className="flex-1 flex items-start px-3 py-1.5">
|
|
@@ -269,14 +278,6 @@ export function SessionList(props: SessionListProps) {
|
|
|
269
278
|
);
|
|
270
279
|
}
|
|
271
280
|
|
|
272
|
-
var displayed = props.filter
|
|
273
|
-
? sessions.filter(function (s) {
|
|
274
|
-
return s.title.toLowerCase().includes(props.filter!.toLowerCase());
|
|
275
|
-
})
|
|
276
|
-
: sessions;
|
|
277
|
-
|
|
278
|
-
var grouped = groupByTime(displayed);
|
|
279
|
-
|
|
280
281
|
return (
|
|
281
282
|
<div className="flex flex-col flex-1 overflow-hidden min-h-0">
|
|
282
283
|
<div className="flex-1 overflow-y-auto overflow-x-hidden scrollbar-hidden py-0.5 pb-16">
|
package/client/src/router.tsx
CHANGED
|
@@ -83,7 +83,10 @@ function LoadingScreen() {
|
|
|
83
83
|
var logoRight = centerX + logoHalf;
|
|
84
84
|
var logoBottom = centerY + logoHalf;
|
|
85
85
|
|
|
86
|
-
var
|
|
86
|
+
var computedPrimary = getComputedStyle(document.documentElement).getPropertyValue("--color-primary").trim();
|
|
87
|
+
var primary = computedPrimary ? "oklch(" + computedPrimary + ")" : "oklch(55% 0.25 280)";
|
|
88
|
+
|
|
89
|
+
var prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
87
90
|
|
|
88
91
|
type Dot = { x: number; y: number; col: number; row: number; hidden: boolean; brightness: number };
|
|
89
92
|
var dots: Dot[] = [];
|
|
@@ -123,6 +126,37 @@ function LoadingScreen() {
|
|
|
123
126
|
return result;
|
|
124
127
|
}
|
|
125
128
|
|
|
129
|
+
function drawStatic() {
|
|
130
|
+
ctx!.clearRect(0, 0, canvasSize, canvasSize);
|
|
131
|
+
for (var di = 0; di < dots.length; di++) {
|
|
132
|
+
var dot = dots[di];
|
|
133
|
+
if (dot.hidden) continue;
|
|
134
|
+
ctx!.beginPath();
|
|
135
|
+
ctx!.arc(dot.x, dot.y, dotRadius, 0, Math.PI * 2);
|
|
136
|
+
ctx!.fillStyle = primary;
|
|
137
|
+
ctx!.globalAlpha = 0.15;
|
|
138
|
+
ctx!.fill();
|
|
139
|
+
ctx!.globalAlpha = 1.0;
|
|
140
|
+
}
|
|
141
|
+
var squares = [
|
|
142
|
+
[logoLeft, logoTop],
|
|
143
|
+
[logoLeft + logoSquare + logoGap, logoTop],
|
|
144
|
+
[logoLeft, logoTop + logoSquare + logoGap],
|
|
145
|
+
[logoLeft + logoSquare + logoGap, logoTop + logoSquare + logoGap],
|
|
146
|
+
];
|
|
147
|
+
for (var si = 0; si < squares.length; si++) {
|
|
148
|
+
ctx!.fillStyle = primary;
|
|
149
|
+
ctx!.globalAlpha = 0.9;
|
|
150
|
+
ctx!.fillRect(squares[si][0], squares[si][1], logoSquare, logoSquare);
|
|
151
|
+
ctx!.globalAlpha = 1.0;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (prefersReducedMotion) {
|
|
156
|
+
drawStatic();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
126
160
|
function animate() {
|
|
127
161
|
now = performance.now();
|
|
128
162
|
ctx!.clearRect(0, 0, canvasSize, canvasSize);
|
|
@@ -277,8 +311,17 @@ function RemoveProjectConfirm() {
|
|
|
277
311
|
}
|
|
278
312
|
})();
|
|
279
313
|
|
|
314
|
+
useEffect(function () {
|
|
315
|
+
if (!slug) return;
|
|
316
|
+
function handleKeyDown(e: KeyboardEvent) {
|
|
317
|
+
if (e.key === "Escape") sidebar.closeConfirmRemove();
|
|
318
|
+
}
|
|
319
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
320
|
+
return function () { document.removeEventListener("keydown", handleKeyDown); };
|
|
321
|
+
}, [slug]);
|
|
322
|
+
|
|
280
323
|
return (
|
|
281
|
-
<div className="fixed inset-0 z-[9999] flex items-center justify-center">
|
|
324
|
+
<div className="fixed inset-0 z-[9999] flex items-center justify-center" role="dialog" aria-modal="true" aria-label="Remove Project">
|
|
282
325
|
<div className="absolute inset-0 bg-black/50" onClick={sidebar.closeConfirmRemove} />
|
|
283
326
|
<div className="relative bg-base-200 border border-base-content/15 rounded-2xl shadow-2xl w-full max-w-sm mx-4 overflow-hidden">
|
|
284
327
|
<div className="px-5 py-4 border-b border-base-content/15">
|
|
@@ -369,6 +412,9 @@ function RootLayout() {
|
|
|
369
412
|
|
|
370
413
|
return (
|
|
371
414
|
<div className="flex w-full h-full overflow-hidden bg-base-100">
|
|
415
|
+
<a href="#main-content" className="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[99999] focus:px-4 focus:py-2 focus:bg-primary focus:text-primary-content focus:rounded-lg focus:text-sm focus:font-semibold">
|
|
416
|
+
Skip to content
|
|
417
|
+
</a>
|
|
372
418
|
<LoadingScreen />
|
|
373
419
|
<div className="drawer lg:drawer-open h-full w-full">
|
|
374
420
|
<input
|
|
@@ -376,12 +422,12 @@ function RootLayout() {
|
|
|
376
422
|
type="checkbox"
|
|
377
423
|
className="drawer-toggle"
|
|
378
424
|
checked={sidebar.drawerOpen}
|
|
379
|
-
|
|
425
|
+
readOnly
|
|
380
426
|
/>
|
|
381
427
|
|
|
382
|
-
<
|
|
428
|
+
<main id="main-content" className="drawer-content flex flex-col h-full min-w-0 overflow-hidden">
|
|
383
429
|
<Outlet />
|
|
384
|
-
</
|
|
430
|
+
</main>
|
|
385
431
|
|
|
386
432
|
<div ref={drawerSideRef} className="drawer-side z-50 h-full">
|
|
387
433
|
<label
|
|
@@ -390,9 +436,9 @@ function RootLayout() {
|
|
|
390
436
|
className="drawer-overlay"
|
|
391
437
|
onClick={closeDrawer}
|
|
392
438
|
/>
|
|
393
|
-
<
|
|
439
|
+
<nav aria-label="Sidebar navigation" className="h-full w-full lg:w-[284px] flex flex-col overflow-hidden">
|
|
394
440
|
<Sidebar onSessionSelect={closeDrawer} />
|
|
395
|
-
</
|
|
441
|
+
</nav>
|
|
396
442
|
</div>
|
|
397
443
|
</div>
|
|
398
444
|
<NodeSettingsModal
|
|
@@ -408,24 +454,71 @@ function RootLayout() {
|
|
|
408
454
|
);
|
|
409
455
|
}
|
|
410
456
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
457
|
+
import { Component } from "react";
|
|
458
|
+
import type { ReactNode, ErrorInfo } from "react";
|
|
459
|
+
import { AlertTriangle, RefreshCw } from "lucide-react";
|
|
460
|
+
|
|
461
|
+
class ViewErrorBoundary extends Component<{ children: ReactNode; viewName: string }, { hasError: boolean; error: Error | null }> {
|
|
462
|
+
constructor(props: { children: ReactNode; viewName: string }) {
|
|
463
|
+
super(props);
|
|
464
|
+
this.state = { hasError: false, error: null };
|
|
415
465
|
}
|
|
416
|
-
|
|
417
|
-
return
|
|
466
|
+
static getDerivedStateFromError(error: Error) {
|
|
467
|
+
return { hasError: true, error: error };
|
|
418
468
|
}
|
|
419
|
-
|
|
420
|
-
|
|
469
|
+
componentDidCatch(error: Error, info: ErrorInfo) {
|
|
470
|
+
console.error("[lattice] View error in " + this.props.viewName + ":", error, info.componentStack);
|
|
421
471
|
}
|
|
422
|
-
|
|
423
|
-
|
|
472
|
+
render() {
|
|
473
|
+
if (this.state.hasError) {
|
|
474
|
+
var self = this;
|
|
475
|
+
return (
|
|
476
|
+
<div className="flex flex-col items-center justify-center h-full bg-base-100 bg-lattice-grid gap-4 p-8">
|
|
477
|
+
<AlertTriangle size={32} className="text-warning/50" />
|
|
478
|
+
<div className="text-center max-w-[500px]">
|
|
479
|
+
<p className="text-[14px] font-mono text-base-content/60 mb-1">
|
|
480
|
+
Error in {this.props.viewName}
|
|
481
|
+
</p>
|
|
482
|
+
<p className="text-[12px] text-base-content/30 mb-4 font-mono break-all">
|
|
483
|
+
{this.state.error?.message || "Unknown error"}
|
|
484
|
+
</p>
|
|
485
|
+
<button
|
|
486
|
+
onClick={function () { self.setState({ hasError: false, error: null }); }}
|
|
487
|
+
className="btn btn-ghost btn-sm text-[12px] gap-1.5"
|
|
488
|
+
>
|
|
489
|
+
<RefreshCw size={12} />
|
|
490
|
+
Retry
|
|
491
|
+
</button>
|
|
492
|
+
</div>
|
|
493
|
+
</div>
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
return this.props.children;
|
|
424
497
|
}
|
|
425
|
-
|
|
426
|
-
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function IndexPage() {
|
|
501
|
+
var sidebar = useSidebar();
|
|
502
|
+
var viewName = sidebar.activeView.type;
|
|
503
|
+
var content;
|
|
504
|
+
if (viewName === "dashboard") {
|
|
505
|
+
content = <DashboardView />;
|
|
506
|
+
} else if (viewName === "settings") {
|
|
507
|
+
content = <SettingsView />;
|
|
508
|
+
} else if (viewName === "project-settings") {
|
|
509
|
+
content = <ProjectSettingsView />;
|
|
510
|
+
} else if (viewName === "project-dashboard") {
|
|
511
|
+
content = <ProjectDashboardView />;
|
|
512
|
+
} else if (viewName === "analytics") {
|
|
513
|
+
content = <AnalyticsView />;
|
|
514
|
+
} else {
|
|
515
|
+
content = <WorkspaceView />;
|
|
427
516
|
}
|
|
428
|
-
return
|
|
517
|
+
return (
|
|
518
|
+
<ViewErrorBoundary viewName={viewName}>
|
|
519
|
+
{content}
|
|
520
|
+
</ViewErrorBoundary>
|
|
521
|
+
);
|
|
429
522
|
}
|
|
430
523
|
|
|
431
524
|
var rootRoute = createRootRoute({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.6",
|
|
4
4
|
"description": "Multi-machine agentic dashboard for Claude Code. Monitor sessions, manage MCP servers and skills, orchestrate across mesh-networked nodes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Aaron Scherer <me@aaronscherer.me>",
|
|
@@ -439,6 +439,13 @@ export function startChatStream(options: ChatStreamOptions): void {
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
+
if (toolName === "Bash") {
|
|
443
|
+
var cmd = ((input.command || "") as string).trim();
|
|
444
|
+
if (cmd.startsWith("cd ")) {
|
|
445
|
+
return Promise.resolve({ behavior: "allow", updatedInput: input, toolUseID: options.toolUseID } as PermissionResult);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
442
449
|
var allowRules: string[] = [];
|
|
443
450
|
if (existsSync(projectSettingsPath)) {
|
|
444
451
|
try {
|