@cryptiklemur/lattice 1.9.0 → 1.10.1

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,3 +1,5 @@
1
+ import { useState, useEffect, useRef } from "react";
2
+ import { Maximize2, Minimize2 } from "lucide-react";
1
3
  import type { ReactNode } from "react";
2
4
 
3
5
  interface ChartCardProps {
@@ -7,16 +9,156 @@ interface ChartCardProps {
7
9
  action?: ReactNode;
8
10
  }
9
11
 
10
- export function ChartCard({ title, children, className, action }: ChartCardProps) {
11
- return (
12
- <div className={["rounded-xl border border-base-content/8 bg-base-300/50 p-4", className].filter(Boolean).join(" ")}>
12
+ export function ChartCard(props: ChartCardProps) {
13
+ var [isFullscreen, setIsFullscreen] = useState(false);
14
+ var cardRef = useRef<HTMLDivElement>(null);
15
+ var [originRect, setOriginRect] = useState<DOMRect | null>(null);
16
+ var [animating, setAnimating] = useState(false);
17
+
18
+ function openFullscreen() {
19
+ if (cardRef.current) {
20
+ setOriginRect(cardRef.current.getBoundingClientRect());
21
+ }
22
+ setAnimating(true);
23
+ setIsFullscreen(true);
24
+ requestAnimationFrame(function () {
25
+ requestAnimationFrame(function () {
26
+ setAnimating(false);
27
+ });
28
+ });
29
+ }
30
+
31
+ function closeFullscreen() {
32
+ setAnimating(true);
33
+ setTimeout(function () {
34
+ setIsFullscreen(false);
35
+ setAnimating(false);
36
+ setOriginRect(null);
37
+ }, 250);
38
+ }
39
+
40
+ useEffect(function () {
41
+ if (!isFullscreen) return;
42
+ function handleKeyDown(e: KeyboardEvent) {
43
+ if (e.key === "Escape") closeFullscreen();
44
+ }
45
+ document.addEventListener("keydown", handleKeyDown);
46
+ return function () { document.removeEventListener("keydown", handleKeyDown); };
47
+ }, [isFullscreen]);
48
+
49
+ useEffect(function () {
50
+ if (isFullscreen) {
51
+ document.body.style.overflow = "hidden";
52
+ } else {
53
+ document.body.style.overflow = "";
54
+ }
55
+ return function () { document.body.style.overflow = ""; };
56
+ }, [isFullscreen]);
57
+
58
+ var cardContent = (
59
+ <>
13
60
  <div className="flex items-center justify-between mb-4">
14
61
  <span className="text-[10px] font-mono font-bold uppercase tracking-widest text-base-content/35">
15
- {title}
62
+ {props.title}
16
63
  </span>
17
- {action && <div>{action}</div>}
64
+ <div className="flex items-center gap-2">
65
+ {props.action && <div>{props.action}</div>}
66
+ <button
67
+ onClick={function () {
68
+ if (isFullscreen) {
69
+ closeFullscreen();
70
+ } else {
71
+ openFullscreen();
72
+ }
73
+ }}
74
+ className="text-base-content/20 hover:text-base-content/50 transition-colors cursor-pointer p-0.5 rounded hover:bg-base-content/5"
75
+ aria-label={isFullscreen ? "Exit fullscreen" : "Fullscreen"}
76
+ title={isFullscreen ? "Exit fullscreen (Esc)" : "Fullscreen"}
77
+ >
78
+ {isFullscreen ? <Minimize2 size={12} /> : <Maximize2 size={12} />}
79
+ </button>
80
+ </div>
18
81
  </div>
19
- {children}
82
+ {props.children}
83
+ </>
84
+ );
85
+
86
+ if (isFullscreen) {
87
+ var overlayStyle: React.CSSProperties = {
88
+ transition: "opacity 250ms cubic-bezier(0.4, 0, 0.2, 1)",
89
+ opacity: animating ? 0 : 1,
90
+ };
91
+
92
+ var modalStyle: React.CSSProperties = {
93
+ transition: "all 250ms cubic-bezier(0.4, 0, 0.2, 1)",
94
+ };
95
+
96
+ if (animating && originRect) {
97
+ modalStyle.position = "fixed";
98
+ modalStyle.top = originRect.top + "px";
99
+ modalStyle.left = originRect.left + "px";
100
+ modalStyle.width = originRect.width + "px";
101
+ modalStyle.height = originRect.height + "px";
102
+ modalStyle.opacity = 0;
103
+ }
104
+
105
+ return (
106
+ <>
107
+ <div
108
+ ref={cardRef}
109
+ className={"rounded-xl border border-base-content/8 bg-base-300/50 p-4 invisible " + (props.className || "")}
110
+ >
111
+ {cardContent}
112
+ </div>
113
+
114
+ <div
115
+ className="fixed inset-0 z-[9998] bg-black/60 backdrop-blur-sm"
116
+ style={overlayStyle}
117
+ onClick={closeFullscreen}
118
+ />
119
+ <div
120
+ className="fixed inset-0 z-[9999] flex items-center justify-center p-8"
121
+ style={{ pointerEvents: "none" }}
122
+ >
123
+ <div
124
+ className="w-full max-w-[1100px] rounded-2xl border border-base-content/10 bg-base-200 shadow-2xl overflow-hidden flex flex-col"
125
+ style={Object.assign(
126
+ { maxHeight: "65vh", pointerEvents: "auto" as const },
127
+ animating
128
+ ? { opacity: 0, transform: "scale(0.95)", transition: "all 250ms cubic-bezier(0.4, 0, 0.2, 1)" }
129
+ : { opacity: 1, transform: "scale(1)", transition: "all 250ms cubic-bezier(0.4, 0, 0.2, 1)" }
130
+ )}
131
+ >
132
+ <div className="flex items-center justify-between px-6 py-3 border-b border-base-content/8 flex-shrink-0">
133
+ <span className="text-[12px] font-mono font-bold uppercase tracking-widest text-base-content/50">
134
+ {props.title}
135
+ </span>
136
+ <div className="flex items-center gap-3">
137
+ {props.action && <div>{props.action}</div>}
138
+ <button
139
+ onClick={closeFullscreen}
140
+ className="text-base-content/30 hover:text-base-content/60 transition-colors cursor-pointer p-1 rounded-lg hover:bg-base-content/5"
141
+ aria-label="Exit fullscreen"
142
+ >
143
+ <Minimize2 size={16} />
144
+ </button>
145
+ </div>
146
+ </div>
147
+ <div className="flex-1 p-6 overflow-auto min-h-0 [&_.recharts-responsive-container]:!h-full [&_.recharts-responsive-container]:!min-h-[350px]">
148
+ {props.children}
149
+ </div>
150
+ </div>
151
+ </div>
152
+ </>
153
+ );
154
+ }
155
+
156
+ return (
157
+ <div
158
+ ref={cardRef}
159
+ className={"rounded-xl border border-base-content/8 bg-base-300/50 p-4 " + (props.className || "")}
160
+ >
161
+ {cardContent}
20
162
  </div>
21
163
  );
22
164
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
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>",