@khal-os/ui 1.0.0 → 1.0.2
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.
- package/LICENSE +94 -0
- package/README.md +25 -0
- package/dist/index.cjs +2661 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +926 -0
- package/dist/index.d.ts +926 -0
- package/dist/index.js +2510 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -40
- package/tokens.css +260 -238
- package/src/components/ContextMenu.tsx +0 -130
- package/src/components/avatar.tsx +0 -71
- package/src/components/badge.tsx +0 -39
- package/src/components/button.tsx +0 -102
- package/src/components/command.tsx +0 -165
- package/src/components/cost-counter.tsx +0 -75
- package/src/components/data-row.tsx +0 -97
- package/src/components/dropdown-menu.tsx +0 -233
- package/src/components/glass-card.tsx +0 -74
- package/src/components/input.tsx +0 -48
- package/src/components/khal-logo.tsx +0 -73
- package/src/components/live-feed.tsx +0 -109
- package/src/components/mesh-gradient.tsx +0 -57
- package/src/components/metric-display.tsx +0 -93
- package/src/components/note.tsx +0 -55
- package/src/components/number-flow.tsx +0 -25
- package/src/components/pill-badge.tsx +0 -65
- package/src/components/progress-bar.tsx +0 -70
- package/src/components/section-card.tsx +0 -76
- package/src/components/separator.tsx +0 -25
- package/src/components/spinner.tsx +0 -42
- package/src/components/status-dot.tsx +0 -90
- package/src/components/switch.tsx +0 -36
- package/src/components/theme-provider.tsx +0 -58
- package/src/components/theme-switcher.tsx +0 -59
- package/src/components/ticker-bar.tsx +0 -41
- package/src/components/tooltip.tsx +0 -62
- package/src/components/window-minimized-context.tsx +0 -29
- package/src/hooks/useReducedMotion.ts +0 -21
- package/src/index.ts +0 -58
- package/src/lib/animations.ts +0 -50
- package/src/primitives/collapsible-sidebar.tsx +0 -226
- package/src/primitives/dialog.tsx +0 -76
- package/src/primitives/empty-state.tsx +0 -43
- package/src/primitives/index.ts +0 -22
- package/src/primitives/list-view.tsx +0 -155
- package/src/primitives/property-panel.tsx +0 -108
- package/src/primitives/section-header.tsx +0 -19
- package/src/primitives/sidebar-nav.tsx +0 -110
- package/src/primitives/split-pane.tsx +0 -146
- package/src/primitives/status-badge.tsx +0 -10
- package/src/primitives/status-bar.tsx +0 -100
- package/src/primitives/toolbar.tsx +0 -152
- package/src/server.ts +0 -4
- package/src/stores/notification-store.ts +0 -271
- package/src/stores/theme-store.ts +0 -33
- package/src/tokens/lp-tokens.ts +0 -36
- package/src/utils.ts +0 -6
- package/tsconfig.json +0 -17
package/dist/index.js
ADDED
|
@@ -0,0 +1,2510 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { SUBJECTS, useKhalAuth, useKhalAuth as useKhalAuth2, useNats, useNatsSubscription } from "@khal-os/sdk/app";
|
|
3
|
+
|
|
4
|
+
// src/components/avatar.tsx
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
// src/utils.ts
|
|
8
|
+
import { clsx } from "clsx";
|
|
9
|
+
import { twMerge } from "tailwind-merge";
|
|
10
|
+
function cn(...inputs) {
|
|
11
|
+
return twMerge(clsx(inputs));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/components/status-dot.tsx
|
|
15
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
var sizeMap = { sm: 8, md: 10, lg: 12 };
|
|
17
|
+
var stateConfig = {
|
|
18
|
+
live: { color: "#22c55e", label: "Live", pulse: true },
|
|
19
|
+
online: { color: "#22c55e", label: "Online", pulse: false },
|
|
20
|
+
active: { color: "#22c55e", label: "Active", pulse: true },
|
|
21
|
+
working: { color: "#f59e0b", label: "Working", pulse: true },
|
|
22
|
+
idle: { color: "#64748b", label: "Idle", pulse: false },
|
|
23
|
+
away: { color: "#64748b", label: "Away", pulse: false },
|
|
24
|
+
queued: { color: "#f59e0b", label: "Queued", pulse: false },
|
|
25
|
+
error: { color: "#ef4444", label: "Error", pulse: true }
|
|
26
|
+
};
|
|
27
|
+
function StatusDot({
|
|
28
|
+
state,
|
|
29
|
+
color: colorProp,
|
|
30
|
+
pulse: pulseProp,
|
|
31
|
+
size = "md",
|
|
32
|
+
label: labelProp,
|
|
33
|
+
showLabel = false,
|
|
34
|
+
className,
|
|
35
|
+
style,
|
|
36
|
+
...props
|
|
37
|
+
}) {
|
|
38
|
+
const config = state ? stateConfig[state] : null;
|
|
39
|
+
const color = colorProp ?? config?.color ?? "#64748b";
|
|
40
|
+
const pulse = pulseProp ?? config?.pulse ?? false;
|
|
41
|
+
const label = labelProp ?? config?.label;
|
|
42
|
+
const px = sizeMap[size];
|
|
43
|
+
return /* @__PURE__ */ jsxs(
|
|
44
|
+
"span",
|
|
45
|
+
{
|
|
46
|
+
role: "status",
|
|
47
|
+
"aria-label": label,
|
|
48
|
+
className: cn("relative inline-flex shrink-0 items-center gap-1.5", className),
|
|
49
|
+
style,
|
|
50
|
+
...props,
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ jsxs("span", { className: "relative inline-flex shrink-0", style: { width: px, height: px }, children: [
|
|
53
|
+
pulse && /* @__PURE__ */ jsx(
|
|
54
|
+
"span",
|
|
55
|
+
{
|
|
56
|
+
className: "absolute -inset-0.5 rounded-full",
|
|
57
|
+
style: {
|
|
58
|
+
backgroundColor: color,
|
|
59
|
+
opacity: 0.35,
|
|
60
|
+
animation: "khal-pulse 2s ease-in-out infinite"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
),
|
|
64
|
+
/* @__PURE__ */ jsx(
|
|
65
|
+
"span",
|
|
66
|
+
{
|
|
67
|
+
className: "absolute inset-0 rounded-full",
|
|
68
|
+
style: {
|
|
69
|
+
backgroundColor: color,
|
|
70
|
+
boxShadow: pulse ? `0 0 ${px}px ${color}80` : void 0
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
] }),
|
|
75
|
+
showLabel && label && /* @__PURE__ */ jsx("span", { className: "text-[11px] leading-none", style: { color }, children: label })
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/components/avatar.tsx
|
|
82
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
83
|
+
var sizeMap2 = { sm: 24, md: 32, lg: 40 };
|
|
84
|
+
var fontSizeMap = { sm: "10px", md: "12px", lg: "14px" };
|
|
85
|
+
var statusColorMap = {
|
|
86
|
+
online: "var(--khal-status-live)",
|
|
87
|
+
idle: "var(--khal-status-warning)",
|
|
88
|
+
away: "var(--khal-status-idle)"
|
|
89
|
+
};
|
|
90
|
+
function getInitials(name) {
|
|
91
|
+
const parts = name.trim().split(/\s+/);
|
|
92
|
+
if (parts.length >= 2) {
|
|
93
|
+
return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
|
|
94
|
+
}
|
|
95
|
+
return name.charAt(0).toUpperCase();
|
|
96
|
+
}
|
|
97
|
+
var Avatar = React.forwardRef(
|
|
98
|
+
({ name, size = "md", status, src, className, style, ...props }, ref) => {
|
|
99
|
+
const px = sizeMap2[size];
|
|
100
|
+
const [imgError, setImgError] = React.useState(false);
|
|
101
|
+
const showImage = src && !imgError;
|
|
102
|
+
return /* @__PURE__ */ jsxs2(
|
|
103
|
+
"div",
|
|
104
|
+
{
|
|
105
|
+
ref,
|
|
106
|
+
className: cn("relative inline-flex shrink-0", className),
|
|
107
|
+
style: { width: px, height: px, ...style },
|
|
108
|
+
...props,
|
|
109
|
+
children: [
|
|
110
|
+
showImage ? /* @__PURE__ */ jsx2(
|
|
111
|
+
"img",
|
|
112
|
+
{
|
|
113
|
+
src,
|
|
114
|
+
alt: name,
|
|
115
|
+
className: "h-full w-full rounded-full object-cover",
|
|
116
|
+
onError: () => setImgError(true)
|
|
117
|
+
}
|
|
118
|
+
) : /* @__PURE__ */ jsx2(
|
|
119
|
+
"div",
|
|
120
|
+
{
|
|
121
|
+
className: "flex h-full w-full select-none items-center justify-center rounded-full border font-medium [background:var(--khal-surface-raised)] [border-color:var(--khal-border-subtle)]",
|
|
122
|
+
style: { fontSize: fontSizeMap[size] },
|
|
123
|
+
children: getInitials(name)
|
|
124
|
+
}
|
|
125
|
+
),
|
|
126
|
+
status && /* @__PURE__ */ jsx2("div", { className: "absolute -bottom-px -right-px", children: /* @__PURE__ */ jsx2(StatusDot, { color: statusColorMap[status], size: "sm", label: status, pulse: status === "online" }) })
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
Avatar.displayName = "Avatar";
|
|
133
|
+
|
|
134
|
+
// src/components/badge.tsx
|
|
135
|
+
import { cva } from "class-variance-authority";
|
|
136
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
137
|
+
var badgeVariants = cva(
|
|
138
|
+
"inline-flex items-center rounded-full border font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-blue-700 focus:ring-offset-2",
|
|
139
|
+
{
|
|
140
|
+
variants: {
|
|
141
|
+
variant: {
|
|
142
|
+
gray: "border-gray-alpha-400 bg-gray-100 text-gray-900",
|
|
143
|
+
blue: "border-blue-300 bg-blue-100 text-blue-900",
|
|
144
|
+
green: "border-green-300 bg-green-100 text-green-900",
|
|
145
|
+
amber: "border-amber-300 bg-amber-100 text-amber-900",
|
|
146
|
+
red: "border-red-300 bg-red-100 text-red-900",
|
|
147
|
+
purple: "border-purple-300 bg-purple-100 text-purple-900",
|
|
148
|
+
pink: "border-pink-300 bg-pink-100 text-pink-900",
|
|
149
|
+
teal: "border-teal-300 bg-teal-100 text-teal-900"
|
|
150
|
+
},
|
|
151
|
+
size: {
|
|
152
|
+
sm: "px-2 py-0 text-[11px] leading-[18px]",
|
|
153
|
+
md: "px-2.5 py-0.5 text-[12px] leading-[18px]"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
defaultVariants: {
|
|
157
|
+
variant: "gray",
|
|
158
|
+
size: "md"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
function Badge({ className, variant, size, contrast, ...props }) {
|
|
163
|
+
return /* @__PURE__ */ jsx3("span", { className: cn(badgeVariants({ variant, size }), className), ...props });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/components/button.tsx
|
|
167
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
168
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
169
|
+
import * as React2 from "react";
|
|
170
|
+
|
|
171
|
+
// src/components/spinner.tsx
|
|
172
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
173
|
+
var sizeMap3 = {
|
|
174
|
+
sm: 16,
|
|
175
|
+
md: 20,
|
|
176
|
+
lg: 24
|
|
177
|
+
};
|
|
178
|
+
function Spinner({ size = "md", className, ...props }) {
|
|
179
|
+
const px = sizeMap3[size];
|
|
180
|
+
return /* @__PURE__ */ jsx4(
|
|
181
|
+
"div",
|
|
182
|
+
{
|
|
183
|
+
role: "status",
|
|
184
|
+
"aria-label": "Loading",
|
|
185
|
+
className: cn("inline-flex items-center justify-center", className),
|
|
186
|
+
...props,
|
|
187
|
+
children: /* @__PURE__ */ jsxs3("svg", { width: px, height: px, viewBox: "0 0 20 20", fill: "none", className: "animate-spin", children: [
|
|
188
|
+
/* @__PURE__ */ jsx4("circle", { cx: "10", cy: "10", r: "8", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", opacity: "0.25" }),
|
|
189
|
+
/* @__PURE__ */ jsx4(
|
|
190
|
+
"circle",
|
|
191
|
+
{
|
|
192
|
+
cx: "10",
|
|
193
|
+
cy: "10",
|
|
194
|
+
r: "8",
|
|
195
|
+
stroke: "currentColor",
|
|
196
|
+
strokeWidth: "2",
|
|
197
|
+
strokeLinecap: "round",
|
|
198
|
+
strokeDasharray: "50.26",
|
|
199
|
+
strokeDashoffset: "37.7"
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
] })
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/components/button.tsx
|
|
208
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
209
|
+
var buttonVariants = cva2(
|
|
210
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--khal-radius-button,10px)] text-copy-13 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--khal-accent-primary)] focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 cursor-pointer",
|
|
211
|
+
{
|
|
212
|
+
variants: {
|
|
213
|
+
variant: {
|
|
214
|
+
default: "bg-gray-1000 text-white [color:white] dark:text-black dark:[color:black] hover:bg-gray-900",
|
|
215
|
+
secondary: "bg-background-100 text-gray-1000 border border-gray-alpha-400 hover:bg-gray-alpha-100",
|
|
216
|
+
tertiary: "bg-transparent text-gray-1000 hover:bg-gray-alpha-200",
|
|
217
|
+
error: "bg-red-700 text-white [color:white] hover:bg-red-600",
|
|
218
|
+
warning: "bg-amber-700 text-white [color:white] hover:bg-amber-600",
|
|
219
|
+
ghost: "hover:bg-gray-alpha-200 text-gray-1000",
|
|
220
|
+
link: "text-blue-700 underline-offset-4 hover:underline"
|
|
221
|
+
},
|
|
222
|
+
size: {
|
|
223
|
+
small: "h-8 px-3 text-copy-13",
|
|
224
|
+
medium: "h-9 px-4 text-copy-13",
|
|
225
|
+
large: "h-10 px-5 text-copy-14",
|
|
226
|
+
icon: "h-9 w-9"
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
defaultVariants: {
|
|
230
|
+
variant: "default",
|
|
231
|
+
size: "medium"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
var Button = React2.forwardRef(
|
|
236
|
+
({
|
|
237
|
+
className,
|
|
238
|
+
variant,
|
|
239
|
+
size,
|
|
240
|
+
asChild = false,
|
|
241
|
+
loading = false,
|
|
242
|
+
prefix,
|
|
243
|
+
suffix,
|
|
244
|
+
typeName,
|
|
245
|
+
type,
|
|
246
|
+
disabled,
|
|
247
|
+
children,
|
|
248
|
+
...props
|
|
249
|
+
}, ref) => {
|
|
250
|
+
let resolvedVariant = variant;
|
|
251
|
+
if (!resolvedVariant && type) {
|
|
252
|
+
if (type === "invert") resolvedVariant = "default";
|
|
253
|
+
else if (type === "shadow") resolvedVariant = "secondary";
|
|
254
|
+
else if (type === "unstyled") resolvedVariant = "ghost";
|
|
255
|
+
}
|
|
256
|
+
const Comp = asChild ? Slot : "button";
|
|
257
|
+
return /* @__PURE__ */ jsx5(
|
|
258
|
+
Comp,
|
|
259
|
+
{
|
|
260
|
+
className: cn(buttonVariants({ variant: resolvedVariant, size, className })),
|
|
261
|
+
ref,
|
|
262
|
+
type: typeName ?? "button",
|
|
263
|
+
disabled: disabled || loading,
|
|
264
|
+
...props,
|
|
265
|
+
children: loading ? /* @__PURE__ */ jsx5(Spinner, { size: "sm" }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
266
|
+
prefix && /* @__PURE__ */ jsx5("span", { className: "inline-flex shrink-0", children: prefix }),
|
|
267
|
+
children,
|
|
268
|
+
suffix && /* @__PURE__ */ jsx5("span", { className: "inline-flex shrink-0", children: suffix })
|
|
269
|
+
] })
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
Button.displayName = "Button";
|
|
275
|
+
|
|
276
|
+
// src/components/ContextMenu.tsx
|
|
277
|
+
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
|
|
278
|
+
import * as React3 from "react";
|
|
279
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
280
|
+
var ContextMenu = ContextMenuPrimitive.Root;
|
|
281
|
+
var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
|
|
282
|
+
var ContextMenuGroup = ContextMenuPrimitive.Group;
|
|
283
|
+
var ContextMenuPortal = ContextMenuPrimitive.Portal;
|
|
284
|
+
var ContextMenuSub = ContextMenuPrimitive.Sub;
|
|
285
|
+
var ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
|
286
|
+
var ContextMenuSubTrigger = React3.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
287
|
+
ContextMenuPrimitive.SubTrigger,
|
|
288
|
+
{
|
|
289
|
+
ref,
|
|
290
|
+
className: cn(
|
|
291
|
+
"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-copy-13 outline-none select-none",
|
|
292
|
+
"focus:bg-[var(--khal-menu-hover)]",
|
|
293
|
+
"data-[state=open]:bg-[var(--khal-menu-hover)]",
|
|
294
|
+
inset && "pl-8",
|
|
295
|
+
className
|
|
296
|
+
),
|
|
297
|
+
style: { color: "var(--khal-text-primary)" },
|
|
298
|
+
...props,
|
|
299
|
+
children
|
|
300
|
+
}
|
|
301
|
+
));
|
|
302
|
+
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
|
|
303
|
+
var ContextMenuSubContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
304
|
+
ContextMenuPrimitive.SubContent,
|
|
305
|
+
{
|
|
306
|
+
ref,
|
|
307
|
+
className: cn(
|
|
308
|
+
"z-[9999] min-w-[8rem] overflow-hidden rounded-xl p-1",
|
|
309
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
310
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
311
|
+
className
|
|
312
|
+
),
|
|
313
|
+
style: {
|
|
314
|
+
background: "var(--khal-menu-bg)",
|
|
315
|
+
border: "1px solid var(--khal-menu-border)",
|
|
316
|
+
boxShadow: "var(--khal-menu-shadow)",
|
|
317
|
+
color: "var(--khal-text-primary)"
|
|
318
|
+
},
|
|
319
|
+
...props
|
|
320
|
+
}
|
|
321
|
+
));
|
|
322
|
+
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
|
|
323
|
+
var ContextMenuContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(ContextMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx6(
|
|
324
|
+
ContextMenuPrimitive.Content,
|
|
325
|
+
{
|
|
326
|
+
ref,
|
|
327
|
+
className: cn(
|
|
328
|
+
"z-[9999] min-w-[8rem] overflow-hidden rounded-xl p-1",
|
|
329
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
330
|
+
className
|
|
331
|
+
),
|
|
332
|
+
style: {
|
|
333
|
+
background: "var(--khal-menu-bg)",
|
|
334
|
+
border: "1px solid var(--khal-menu-border)",
|
|
335
|
+
boxShadow: "var(--khal-menu-shadow)",
|
|
336
|
+
color: "var(--khal-text-primary)"
|
|
337
|
+
},
|
|
338
|
+
...props
|
|
339
|
+
}
|
|
340
|
+
) }));
|
|
341
|
+
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
|
|
342
|
+
var ContextMenuItem = React3.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
343
|
+
ContextMenuPrimitive.Item,
|
|
344
|
+
{
|
|
345
|
+
ref,
|
|
346
|
+
className: cn(
|
|
347
|
+
"relative flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-copy-13 outline-none select-none transition-colors",
|
|
348
|
+
"focus:bg-[var(--khal-menu-hover)]",
|
|
349
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
350
|
+
inset && "pl-8",
|
|
351
|
+
className
|
|
352
|
+
),
|
|
353
|
+
style: { color: "var(--khal-text-primary)" },
|
|
354
|
+
...props
|
|
355
|
+
}
|
|
356
|
+
));
|
|
357
|
+
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
|
|
358
|
+
var ContextMenuSeparator = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
359
|
+
ContextMenuPrimitive.Separator,
|
|
360
|
+
{
|
|
361
|
+
ref,
|
|
362
|
+
className: cn("-mx-1 my-1 h-px", className),
|
|
363
|
+
style: { background: "var(--khal-border-default)" },
|
|
364
|
+
...props
|
|
365
|
+
}
|
|
366
|
+
));
|
|
367
|
+
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
|
|
368
|
+
|
|
369
|
+
// src/components/command.tsx
|
|
370
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
371
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
372
|
+
import { Search } from "lucide-react";
|
|
373
|
+
import * as React4 from "react";
|
|
374
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
375
|
+
var Command = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
376
|
+
CommandPrimitive,
|
|
377
|
+
{
|
|
378
|
+
ref,
|
|
379
|
+
className: cn("flex h-full w-full flex-col overflow-hidden rounded-xl bg-background-100 text-gray-1000", className),
|
|
380
|
+
...props
|
|
381
|
+
}
|
|
382
|
+
));
|
|
383
|
+
Command.displayName = CommandPrimitive.displayName;
|
|
384
|
+
function CommandDialog({
|
|
385
|
+
children,
|
|
386
|
+
open,
|
|
387
|
+
onOpenChange,
|
|
388
|
+
...props
|
|
389
|
+
}) {
|
|
390
|
+
return /* @__PURE__ */ jsx7(DialogPrimitive.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs5(DialogPrimitive.Portal, { children: [
|
|
391
|
+
/* @__PURE__ */ jsx7(
|
|
392
|
+
DialogPrimitive.Overlay,
|
|
393
|
+
{
|
|
394
|
+
className: "fixed inset-0 z-[9999] bg-black/40 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
|
|
395
|
+
"cmdk-overlay": ""
|
|
396
|
+
}
|
|
397
|
+
),
|
|
398
|
+
/* @__PURE__ */ jsxs5(
|
|
399
|
+
DialogPrimitive.Content,
|
|
400
|
+
{
|
|
401
|
+
className: "fixed left-[50%] top-[20%] z-[9999] w-full max-w-lg translate-x-[-50%] overflow-hidden rounded-xl shadow-lg",
|
|
402
|
+
style: {
|
|
403
|
+
background: "var(--khal-menu-bg)",
|
|
404
|
+
border: "1px solid var(--khal-menu-border)",
|
|
405
|
+
boxShadow: "var(--khal-menu-shadow)",
|
|
406
|
+
backdropFilter: "blur(24px)",
|
|
407
|
+
WebkitBackdropFilter: "blur(24px)"
|
|
408
|
+
},
|
|
409
|
+
"cmdk-dialog": "",
|
|
410
|
+
"aria-describedby": void 0,
|
|
411
|
+
children: [
|
|
412
|
+
/* @__PURE__ */ jsx7(DialogPrimitive.Title, { className: "sr-only", children: "Command palette" }),
|
|
413
|
+
/* @__PURE__ */ jsx7(
|
|
414
|
+
Command,
|
|
415
|
+
{
|
|
416
|
+
className: "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-gray-700 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3",
|
|
417
|
+
...props,
|
|
418
|
+
children
|
|
419
|
+
}
|
|
420
|
+
)
|
|
421
|
+
]
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
] }) });
|
|
425
|
+
}
|
|
426
|
+
var CommandInput = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs5(
|
|
427
|
+
"div",
|
|
428
|
+
{
|
|
429
|
+
className: "flex items-center px-3",
|
|
430
|
+
style: { borderBottom: "1px solid var(--khal-border-default)" },
|
|
431
|
+
"cmdk-input-wrapper": "",
|
|
432
|
+
children: [
|
|
433
|
+
/* @__PURE__ */ jsx7(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
|
|
434
|
+
/* @__PURE__ */ jsx7(
|
|
435
|
+
CommandPrimitive.Input,
|
|
436
|
+
{
|
|
437
|
+
ref,
|
|
438
|
+
className: cn(
|
|
439
|
+
"flex h-10 w-full rounded-md bg-transparent py-3 text-copy-13 outline-none",
|
|
440
|
+
"placeholder:text-gray-700",
|
|
441
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
442
|
+
className
|
|
443
|
+
),
|
|
444
|
+
...props
|
|
445
|
+
}
|
|
446
|
+
)
|
|
447
|
+
]
|
|
448
|
+
}
|
|
449
|
+
));
|
|
450
|
+
CommandInput.displayName = CommandPrimitive.Input.displayName;
|
|
451
|
+
var CommandList = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
452
|
+
CommandPrimitive.List,
|
|
453
|
+
{
|
|
454
|
+
ref,
|
|
455
|
+
className: cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className),
|
|
456
|
+
...props
|
|
457
|
+
}
|
|
458
|
+
));
|
|
459
|
+
CommandList.displayName = CommandPrimitive.List.displayName;
|
|
460
|
+
var CommandEmpty = React4.forwardRef((props, ref) => /* @__PURE__ */ jsx7(CommandPrimitive.Empty, { ref, className: "py-6 text-center text-copy-13 text-gray-700", ...props }));
|
|
461
|
+
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
|
|
462
|
+
var CommandGroup = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
463
|
+
CommandPrimitive.Group,
|
|
464
|
+
{
|
|
465
|
+
ref,
|
|
466
|
+
className: cn(
|
|
467
|
+
"overflow-hidden p-1 text-gray-1000",
|
|
468
|
+
"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-label-12 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-gray-700",
|
|
469
|
+
className
|
|
470
|
+
),
|
|
471
|
+
...props
|
|
472
|
+
}
|
|
473
|
+
));
|
|
474
|
+
CommandGroup.displayName = CommandPrimitive.Group.displayName;
|
|
475
|
+
var CommandSeparator = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(CommandPrimitive.Separator, { ref, className: cn("-mx-1 h-px bg-gray-alpha-400", className), ...props }));
|
|
476
|
+
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
|
|
477
|
+
var CommandItem = React4.forwardRef(({ className, prefix, callback, onSelect, children, ...props }, ref) => /* @__PURE__ */ jsxs5(
|
|
478
|
+
CommandPrimitive.Item,
|
|
479
|
+
{
|
|
480
|
+
ref,
|
|
481
|
+
onSelect: onSelect ?? callback,
|
|
482
|
+
className: cn(
|
|
483
|
+
"relative flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-copy-13 outline-none select-none",
|
|
484
|
+
"data-[selected=true]:text-gray-1000",
|
|
485
|
+
"data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
486
|
+
className
|
|
487
|
+
),
|
|
488
|
+
...props,
|
|
489
|
+
children: [
|
|
490
|
+
prefix && /* @__PURE__ */ jsx7("span", { className: "inline-flex shrink-0", children: prefix }),
|
|
491
|
+
children
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
));
|
|
495
|
+
CommandItem.displayName = CommandPrimitive.Item.displayName;
|
|
496
|
+
var CommandShortcut = ({ className, ...props }) => {
|
|
497
|
+
return /* @__PURE__ */ jsx7("span", { className: cn("ml-auto text-label-12 tracking-widest text-gray-700", className), ...props });
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// src/components/cost-counter.tsx
|
|
501
|
+
import { motion, useInView } from "motion/react";
|
|
502
|
+
import { useCallback, useRef, useState as useState2 } from "react";
|
|
503
|
+
|
|
504
|
+
// src/components/number-flow.tsx
|
|
505
|
+
import NumberFlowBase from "@number-flow/react";
|
|
506
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
507
|
+
function NumberFlow({ transformTiming, spinTiming, opacityTiming, ...props }) {
|
|
508
|
+
return /* @__PURE__ */ jsx8(
|
|
509
|
+
NumberFlowBase,
|
|
510
|
+
{
|
|
511
|
+
transformTiming: transformTiming ?? { duration: 800, easing: "cubic-bezier(0.34, 1.56, 0.64, 1)" },
|
|
512
|
+
spinTiming: spinTiming ?? { duration: 800, easing: "cubic-bezier(0.34, 1.56, 0.64, 1)" },
|
|
513
|
+
opacityTiming: opacityTiming ?? { duration: 350, easing: "ease-out" },
|
|
514
|
+
willChange: true,
|
|
515
|
+
...props
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/components/cost-counter.tsx
|
|
521
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
522
|
+
function CostCounter({ value, suffix, prefix, label, description, budget, budgetColor, className }) {
|
|
523
|
+
const [displayed, setDisplayed] = useState2(0);
|
|
524
|
+
const triggered = useRef(false);
|
|
525
|
+
const barRef = useRef(null);
|
|
526
|
+
const barInView = useInView(barRef, { once: true, amount: 0.6 });
|
|
527
|
+
const handleViewport = useCallback(() => {
|
|
528
|
+
if (!triggered.current) {
|
|
529
|
+
triggered.current = true;
|
|
530
|
+
setDisplayed(value);
|
|
531
|
+
}
|
|
532
|
+
}, [value]);
|
|
533
|
+
return /* @__PURE__ */ jsxs6(
|
|
534
|
+
motion.div,
|
|
535
|
+
{
|
|
536
|
+
onViewportEnter: handleViewport,
|
|
537
|
+
viewport: { once: true, amount: 0.5 },
|
|
538
|
+
className: cn("flex flex-col gap-3", className),
|
|
539
|
+
children: [
|
|
540
|
+
/* @__PURE__ */ jsxs6(
|
|
541
|
+
"div",
|
|
542
|
+
{
|
|
543
|
+
className: "flex items-baseline tabular-nums font-semibold tracking-tight leading-none",
|
|
544
|
+
style: { fontFamily: "var(--font-display, var(--font-geist-sans))" },
|
|
545
|
+
children: [
|
|
546
|
+
prefix && /* @__PURE__ */ jsx9("span", { className: "text-[0.7em]", children: prefix }),
|
|
547
|
+
/* @__PURE__ */ jsx9(NumberFlow, { value: displayed }),
|
|
548
|
+
suffix && /* @__PURE__ */ jsx9("span", { className: "text-[0.65em] ml-0.5 opacity-70", children: suffix })
|
|
549
|
+
]
|
|
550
|
+
}
|
|
551
|
+
),
|
|
552
|
+
budget != null && /* @__PURE__ */ jsx9("div", { ref: barRef, className: "w-full h-2 rounded-full overflow-hidden bg-[var(--ds-gray-alpha-200)]", children: /* @__PURE__ */ jsx9(
|
|
553
|
+
motion.div,
|
|
554
|
+
{
|
|
555
|
+
initial: { width: 0 },
|
|
556
|
+
animate: barInView ? { width: `${Math.min(budget, 100)}%` } : { width: 0 },
|
|
557
|
+
transition: { duration: 1, ease: [0.22, 1, 0.36, 1], delay: 0.15 },
|
|
558
|
+
className: "h-full rounded-full",
|
|
559
|
+
style: { backgroundColor: budgetColor ?? "var(--ds-accent-warm)" }
|
|
560
|
+
}
|
|
561
|
+
) }),
|
|
562
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-0.5", children: [
|
|
563
|
+
/* @__PURE__ */ jsx9("span", { className: "text-sm font-medium leading-5", children: label }),
|
|
564
|
+
description && /* @__PURE__ */ jsx9("span", { className: "text-xs opacity-50 leading-4", children: description })
|
|
565
|
+
] })
|
|
566
|
+
]
|
|
567
|
+
}
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// src/components/data-row.tsx
|
|
572
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
573
|
+
import * as React5 from "react";
|
|
574
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
575
|
+
var dataRowVariants = cva3("flex items-center gap-3 rounded-lg border border-[#FFFFFF14] font-mono", {
|
|
576
|
+
variants: {
|
|
577
|
+
variant: {
|
|
578
|
+
/** Standard data row — subtle bg */
|
|
579
|
+
default: "bg-[#FFFFFF08] py-2.5 px-3",
|
|
580
|
+
/** Inline/compact — for observability-style data */
|
|
581
|
+
inline: "bg-[#FFFFFF06] py-1.5 px-2.5 text-[11px]",
|
|
582
|
+
/** Nested rule row — for indented rule displays */
|
|
583
|
+
rule: "bg-[#FFFFFF05] py-2.5 px-3"
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
defaultVariants: {
|
|
587
|
+
variant: "default"
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
var DataRow = React5.forwardRef(
|
|
591
|
+
({ className, variant, label, value, accentColor, statusDot, dotColor, tag, tagColor, children, ...props }, ref) => {
|
|
592
|
+
return /* @__PURE__ */ jsxs7("div", { ref, className: cn(dataRowVariants({ variant }), className), ...props, children: [
|
|
593
|
+
statusDot && /* @__PURE__ */ jsx10("span", { className: "size-[6px] shrink-0 rounded-full", style: { backgroundColor: dotColor || "#FFFFFF40" } }),
|
|
594
|
+
tag && /* @__PURE__ */ jsx10(
|
|
595
|
+
"span",
|
|
596
|
+
{
|
|
597
|
+
className: "shrink-0 rounded py-0.5 px-2 text-[10px] font-bold uppercase tracking-wide leading-3.5",
|
|
598
|
+
style: {
|
|
599
|
+
color: tagColor || "var(--color-accent, #D49355)",
|
|
600
|
+
backgroundColor: tagColor ? `color-mix(in srgb, ${tagColor} 10%, transparent)` : "rgba(var(--color-accent-rgb, 212,147,85), 0.1)"
|
|
601
|
+
},
|
|
602
|
+
children: tag
|
|
603
|
+
}
|
|
604
|
+
),
|
|
605
|
+
/* @__PURE__ */ jsx10("span", { className: "text-[12px] text-[#FFFFFFCC] leading-4 min-w-0 truncate", children: label }),
|
|
606
|
+
(value || children) && /* @__PURE__ */ jsx10("span", { className: "grow" }),
|
|
607
|
+
value && /* @__PURE__ */ jsx10("span", { className: "text-[12px] leading-4 tabular-nums shrink-0", style: { color: accentColor || "#FFFFFF99" }, children: value }),
|
|
608
|
+
children
|
|
609
|
+
] });
|
|
610
|
+
}
|
|
611
|
+
);
|
|
612
|
+
DataRow.displayName = "DataRow";
|
|
613
|
+
|
|
614
|
+
// src/components/dropdown-menu.tsx
|
|
615
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
616
|
+
import * as React6 from "react";
|
|
617
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
618
|
+
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
619
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
620
|
+
var DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
621
|
+
var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
622
|
+
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
623
|
+
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
624
|
+
var DropdownMenuSubTrigger = React6.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
625
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
626
|
+
{
|
|
627
|
+
ref,
|
|
628
|
+
className: cn(
|
|
629
|
+
"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-copy-13 outline-none select-none",
|
|
630
|
+
"focus:bg-[var(--khal-menu-hover)]",
|
|
631
|
+
"data-[state=open]:bg-[var(--khal-menu-hover)]",
|
|
632
|
+
inset && "pl-8",
|
|
633
|
+
className
|
|
634
|
+
),
|
|
635
|
+
style: { color: "var(--khal-text-primary)" },
|
|
636
|
+
...props,
|
|
637
|
+
children
|
|
638
|
+
}
|
|
639
|
+
));
|
|
640
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
641
|
+
var DropdownMenuSubContent = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
642
|
+
DropdownMenuPrimitive.SubContent,
|
|
643
|
+
{
|
|
644
|
+
ref,
|
|
645
|
+
className: cn(
|
|
646
|
+
"z-[9999] min-w-[8rem] overflow-hidden rounded-xl p-1",
|
|
647
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
648
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
649
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
650
|
+
className
|
|
651
|
+
),
|
|
652
|
+
style: {
|
|
653
|
+
background: "var(--khal-menu-bg)",
|
|
654
|
+
border: "1px solid var(--khal-menu-border)",
|
|
655
|
+
boxShadow: "var(--khal-menu-shadow)",
|
|
656
|
+
color: "var(--khal-text-primary)"
|
|
657
|
+
},
|
|
658
|
+
...props
|
|
659
|
+
}
|
|
660
|
+
));
|
|
661
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
662
|
+
var DropdownMenuContent = React6.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx11(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx11(
|
|
663
|
+
DropdownMenuPrimitive.Content,
|
|
664
|
+
{
|
|
665
|
+
ref,
|
|
666
|
+
sideOffset,
|
|
667
|
+
className: cn(
|
|
668
|
+
"z-[9999] min-w-[8rem] overflow-hidden rounded-xl p-1",
|
|
669
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
670
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
671
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
672
|
+
className
|
|
673
|
+
),
|
|
674
|
+
style: {
|
|
675
|
+
background: "var(--khal-menu-bg)",
|
|
676
|
+
border: "1px solid var(--khal-menu-border)",
|
|
677
|
+
boxShadow: "var(--khal-menu-shadow)",
|
|
678
|
+
color: "var(--khal-text-primary)"
|
|
679
|
+
},
|
|
680
|
+
...props
|
|
681
|
+
}
|
|
682
|
+
) }));
|
|
683
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
684
|
+
var DropdownMenuItem = React6.forwardRef(({ className, inset, prefix, suffix, children, ...props }, ref) => /* @__PURE__ */ jsxs8(
|
|
685
|
+
DropdownMenuPrimitive.Item,
|
|
686
|
+
{
|
|
687
|
+
ref,
|
|
688
|
+
className: cn(
|
|
689
|
+
"relative flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-copy-13 outline-none select-none transition-colors",
|
|
690
|
+
"focus:bg-[var(--khal-menu-hover)]",
|
|
691
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
692
|
+
inset && "pl-8",
|
|
693
|
+
className
|
|
694
|
+
),
|
|
695
|
+
style: { color: "var(--khal-text-primary)" },
|
|
696
|
+
...props,
|
|
697
|
+
children: [
|
|
698
|
+
prefix && /* @__PURE__ */ jsx11("span", { className: "inline-flex shrink-0", children: prefix }),
|
|
699
|
+
children,
|
|
700
|
+
suffix && /* @__PURE__ */ jsx11("span", { className: "ml-auto inline-flex shrink-0", style: { color: "var(--khal-text-muted)" }, children: suffix })
|
|
701
|
+
]
|
|
702
|
+
}
|
|
703
|
+
));
|
|
704
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
705
|
+
var DropdownMenuCheckboxItem = React6.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs8(
|
|
706
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
707
|
+
{
|
|
708
|
+
ref,
|
|
709
|
+
className: cn(
|
|
710
|
+
"relative flex cursor-default items-center rounded-lg py-1.5 pl-8 pr-2 text-copy-13 outline-none select-none transition-colors",
|
|
711
|
+
"focus:bg-[var(--khal-menu-hover)]",
|
|
712
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
713
|
+
className
|
|
714
|
+
),
|
|
715
|
+
style: { color: "var(--khal-text-primary)" },
|
|
716
|
+
checked,
|
|
717
|
+
...props,
|
|
718
|
+
children: [
|
|
719
|
+
/* @__PURE__ */ jsx11("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx11(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx11("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx11(
|
|
720
|
+
"path",
|
|
721
|
+
{
|
|
722
|
+
d: "M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3354 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.5553 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z",
|
|
723
|
+
fill: "currentColor",
|
|
724
|
+
fillRule: "evenodd",
|
|
725
|
+
clipRule: "evenodd"
|
|
726
|
+
}
|
|
727
|
+
) }) }) }),
|
|
728
|
+
children
|
|
729
|
+
]
|
|
730
|
+
}
|
|
731
|
+
));
|
|
732
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
733
|
+
var DropdownMenuRadioItem = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs8(
|
|
734
|
+
DropdownMenuPrimitive.RadioItem,
|
|
735
|
+
{
|
|
736
|
+
ref,
|
|
737
|
+
className: cn(
|
|
738
|
+
"relative flex cursor-default items-center rounded-lg py-1.5 pl-8 pr-2 text-copy-13 outline-none select-none transition-colors",
|
|
739
|
+
"focus:bg-[var(--khal-menu-hover)]",
|
|
740
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
741
|
+
className
|
|
742
|
+
),
|
|
743
|
+
style: { color: "var(--khal-text-primary)" },
|
|
744
|
+
...props,
|
|
745
|
+
children: [
|
|
746
|
+
/* @__PURE__ */ jsx11("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx11(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx11("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", children: /* @__PURE__ */ jsx11("circle", { cx: "4", cy: "4", r: "4", fill: "currentColor" }) }) }) }),
|
|
747
|
+
children
|
|
748
|
+
]
|
|
749
|
+
}
|
|
750
|
+
));
|
|
751
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
752
|
+
var DropdownMenuLabel = React6.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
753
|
+
DropdownMenuPrimitive.Label,
|
|
754
|
+
{
|
|
755
|
+
ref,
|
|
756
|
+
className: cn("px-2 py-1.5 text-label-12 font-semibold", inset && "pl-8", className),
|
|
757
|
+
style: { color: "var(--khal-text-secondary)" },
|
|
758
|
+
...props
|
|
759
|
+
}
|
|
760
|
+
));
|
|
761
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
762
|
+
var DropdownMenuSeparator = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
763
|
+
DropdownMenuPrimitive.Separator,
|
|
764
|
+
{
|
|
765
|
+
ref,
|
|
766
|
+
className: cn("-mx-1 my-1 h-px", className),
|
|
767
|
+
style: { background: "var(--khal-border-default)" },
|
|
768
|
+
...props
|
|
769
|
+
}
|
|
770
|
+
));
|
|
771
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
772
|
+
var DropdownMenuShortcut = ({ className, ...props }) => {
|
|
773
|
+
return /* @__PURE__ */ jsx11(
|
|
774
|
+
"span",
|
|
775
|
+
{
|
|
776
|
+
className: cn("ml-auto text-label-12 tracking-widest", className),
|
|
777
|
+
style: { color: "var(--khal-text-muted)" },
|
|
778
|
+
...props
|
|
779
|
+
}
|
|
780
|
+
);
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
// src/components/glass-card.tsx
|
|
784
|
+
import { cva as cva4 } from "class-variance-authority";
|
|
785
|
+
import * as React7 from "react";
|
|
786
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
787
|
+
var glassCardVariants = cva4(
|
|
788
|
+
"relative overflow-hidden border transition-all [background:var(--khal-glass-tint)] [border-color:var(--khal-glass-border)]",
|
|
789
|
+
{
|
|
790
|
+
variants: {
|
|
791
|
+
variant: {
|
|
792
|
+
default: "[box-shadow:var(--khal-shadow-sm)]",
|
|
793
|
+
raised: "[box-shadow:var(--khal-shadow-md)]"
|
|
794
|
+
},
|
|
795
|
+
padding: {
|
|
796
|
+
sm: "p-3",
|
|
797
|
+
md: "p-4",
|
|
798
|
+
lg: "p-6"
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
defaultVariants: {
|
|
802
|
+
variant: "default",
|
|
803
|
+
padding: "md"
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
);
|
|
807
|
+
var GlassCard = React7.forwardRef(
|
|
808
|
+
({ className, variant, padding, hover = false, glow, style, children, ...props }, ref) => {
|
|
809
|
+
return /* @__PURE__ */ jsxs9(
|
|
810
|
+
"div",
|
|
811
|
+
{
|
|
812
|
+
ref,
|
|
813
|
+
className: cn(
|
|
814
|
+
glassCardVariants({ variant, padding }),
|
|
815
|
+
hover && [
|
|
816
|
+
"cursor-pointer",
|
|
817
|
+
"hover:-translate-y-0.5",
|
|
818
|
+
"hover:[border-color:var(--khal-border-strong)]",
|
|
819
|
+
"hover:[box-shadow:var(--khal-shadow-lg)]"
|
|
820
|
+
],
|
|
821
|
+
className
|
|
822
|
+
),
|
|
823
|
+
style: {
|
|
824
|
+
backdropFilter: "var(--khal-glass-filter)",
|
|
825
|
+
WebkitBackdropFilter: "var(--khal-glass-filter)",
|
|
826
|
+
borderRadius: "var(--khal-radius-xl)",
|
|
827
|
+
transitionTimingFunction: "var(--khal-ease-spring)",
|
|
828
|
+
transitionDuration: "var(--khal-duration-normal)",
|
|
829
|
+
...style
|
|
830
|
+
},
|
|
831
|
+
...props,
|
|
832
|
+
children: [
|
|
833
|
+
glow && /* @__PURE__ */ jsx12(
|
|
834
|
+
"div",
|
|
835
|
+
{
|
|
836
|
+
className: "pointer-events-none absolute inset-0 rounded-[inherit]",
|
|
837
|
+
style: {
|
|
838
|
+
background: `radial-gradient(ellipse at 50% 0%, color-mix(in srgb, ${glow} 20%, transparent), transparent 70%)`
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
),
|
|
842
|
+
/* @__PURE__ */ jsx12("div", { className: "relative", children })
|
|
843
|
+
]
|
|
844
|
+
}
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
GlassCard.displayName = "GlassCard";
|
|
849
|
+
|
|
850
|
+
// src/components/input.tsx
|
|
851
|
+
import * as React8 from "react";
|
|
852
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
853
|
+
var Input = React8.forwardRef(
|
|
854
|
+
({ className, label, size, typeName, type, ...props }, ref) => {
|
|
855
|
+
const input = /* @__PURE__ */ jsx13(
|
|
856
|
+
"input",
|
|
857
|
+
{
|
|
858
|
+
type: typeName ?? type,
|
|
859
|
+
className: cn(
|
|
860
|
+
"flex w-full rounded-md border border-gray-alpha-400 bg-background-100 px-3 text-copy-13 text-gray-1000 transition-colors",
|
|
861
|
+
"file:border-0 file:bg-transparent file:text-sm file:font-medium",
|
|
862
|
+
"placeholder:text-gray-700",
|
|
863
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-700 focus-visible:ring-offset-1",
|
|
864
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
865
|
+
size === "small" ? "h-8" : size === "large" ? "h-11" : "h-9",
|
|
866
|
+
className
|
|
867
|
+
),
|
|
868
|
+
ref,
|
|
869
|
+
...props
|
|
870
|
+
}
|
|
871
|
+
);
|
|
872
|
+
if (label) {
|
|
873
|
+
return /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1.5", children: [
|
|
874
|
+
/* @__PURE__ */ jsx13("label", { className: "text-label-13 text-gray-900", children: label }),
|
|
875
|
+
input
|
|
876
|
+
] });
|
|
877
|
+
}
|
|
878
|
+
return input;
|
|
879
|
+
}
|
|
880
|
+
);
|
|
881
|
+
Input.displayName = "Input";
|
|
882
|
+
|
|
883
|
+
// src/components/khal-logo.tsx
|
|
884
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
885
|
+
function KhalLogo({ size = 20, variant = "light", className }) {
|
|
886
|
+
const color = variant === "light" ? "#FFFFFF" : "#0A0A0A";
|
|
887
|
+
const showFull = size >= 20;
|
|
888
|
+
if (!showFull) {
|
|
889
|
+
return /* @__PURE__ */ jsx14(
|
|
890
|
+
"svg",
|
|
891
|
+
{
|
|
892
|
+
viewBox: "0 0 156 155",
|
|
893
|
+
fill: "none",
|
|
894
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
895
|
+
width: size,
|
|
896
|
+
height: size,
|
|
897
|
+
className,
|
|
898
|
+
"aria-label": "K",
|
|
899
|
+
style: { color },
|
|
900
|
+
children: /* @__PURE__ */ jsx14(
|
|
901
|
+
"path",
|
|
902
|
+
{
|
|
903
|
+
d: "M0 0H27.4425V65.9519H71.7054L122.829 0H155.362L95.3869 76.1317L164.657 154.92H128.805L72.5913 92.2878H27.4425V154.92H0V0Z",
|
|
904
|
+
fill: "currentColor"
|
|
905
|
+
}
|
|
906
|
+
)
|
|
907
|
+
}
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
return /* @__PURE__ */ jsxs11(
|
|
911
|
+
"svg",
|
|
912
|
+
{
|
|
913
|
+
viewBox: "0 0 756 155",
|
|
914
|
+
fill: "none",
|
|
915
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
916
|
+
width: size * (756 / 155),
|
|
917
|
+
height: size,
|
|
918
|
+
className,
|
|
919
|
+
"aria-label": "khal",
|
|
920
|
+
style: { color },
|
|
921
|
+
children: [
|
|
922
|
+
/* @__PURE__ */ jsxs11("g", { clipPath: "url(#khal-logo-clip)", children: [
|
|
923
|
+
/* @__PURE__ */ jsx14(
|
|
924
|
+
"path",
|
|
925
|
+
{
|
|
926
|
+
d: "M616.81 0H644.252V128.584H765.533V154.92H638.499C635.4 154.92 632.524 154.33 629.867 153.149C627.211 151.969 624.924 150.42 623.007 148.502C621.088 146.584 619.539 144.371 618.359 141.863C617.326 139.206 616.81 136.403 616.81 133.453V0Z",
|
|
927
|
+
fill: "currentColor"
|
|
928
|
+
}
|
|
929
|
+
),
|
|
930
|
+
/* @__PURE__ */ jsx14(
|
|
931
|
+
"path",
|
|
932
|
+
{
|
|
933
|
+
d: "M443.058 21.2467C445.123 14.4594 448.295 9.22105 452.573 5.53287C456.853 1.8447 462.533 0 469.616 0H519.632C527.009 0 532.911 1.91744 537.337 5.75467C541.763 9.44285 545.009 14.6072 547.076 21.2467L589.125 154.92H560.133L546.411 110.657H461.87L468.73 84.3212H538.665L521.181 26.3359H468.951L430 154.92H400.786L443.058 21.2467Z",
|
|
934
|
+
fill: "currentColor"
|
|
935
|
+
}
|
|
936
|
+
),
|
|
937
|
+
/* @__PURE__ */ jsx14(
|
|
938
|
+
"path",
|
|
939
|
+
{
|
|
940
|
+
d: "M190.123 0H217.565V62.6322H344.6V0H372.043V154.92H344.6V88.9681H217.565V154.92H190.123V0Z",
|
|
941
|
+
fill: "currentColor"
|
|
942
|
+
}
|
|
943
|
+
),
|
|
944
|
+
/* @__PURE__ */ jsx14(
|
|
945
|
+
"path",
|
|
946
|
+
{
|
|
947
|
+
d: "M0 0H27.4425V65.9519H71.7054L122.829 0H155.362L95.3869 76.1317L164.657 154.92H128.805L72.5913 92.2878H27.4425V154.92H0V0Z",
|
|
948
|
+
fill: "currentColor"
|
|
949
|
+
}
|
|
950
|
+
)
|
|
951
|
+
] }),
|
|
952
|
+
/* @__PURE__ */ jsx14("defs", { children: /* @__PURE__ */ jsx14("clipPath", { id: "khal-logo-clip", children: /* @__PURE__ */ jsx14("rect", { width: "756", height: "155", fill: "white" }) }) })
|
|
953
|
+
]
|
|
954
|
+
}
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// src/components/live-feed.tsx
|
|
959
|
+
import { AnimatePresence, motion as motion2 } from "motion/react";
|
|
960
|
+
import { useCallback as useCallback2, useEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
961
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
962
|
+
var typeColors = {
|
|
963
|
+
info: "var(--ds-blue-600)",
|
|
964
|
+
success: "var(--ds-green-600)",
|
|
965
|
+
warning: "var(--ds-amber-600)",
|
|
966
|
+
error: "var(--ds-red-600)",
|
|
967
|
+
agent: "var(--ds-purple-600)",
|
|
968
|
+
system: "var(--ds-gray-600)"
|
|
969
|
+
};
|
|
970
|
+
function LiveFeed({
|
|
971
|
+
events: externalEvents,
|
|
972
|
+
maxVisible = 50,
|
|
973
|
+
showTimestamps = true,
|
|
974
|
+
height = 300,
|
|
975
|
+
className
|
|
976
|
+
}) {
|
|
977
|
+
const [events, setEvents] = useState3(externalEvents ?? []);
|
|
978
|
+
const scrollRef = useRef2(null);
|
|
979
|
+
const isAtBottom = useRef2(true);
|
|
980
|
+
useEffect(() => {
|
|
981
|
+
if (externalEvents) {
|
|
982
|
+
setEvents((prev) => {
|
|
983
|
+
const combined = [...prev, ...externalEvents.filter((e) => !prev.some((p) => p.id === e.id))];
|
|
984
|
+
return combined.slice(-maxVisible);
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
}, [externalEvents, maxVisible]);
|
|
988
|
+
useEffect(() => {
|
|
989
|
+
const el = scrollRef.current;
|
|
990
|
+
if (el && isAtBottom.current) {
|
|
991
|
+
el.scrollTop = el.scrollHeight;
|
|
992
|
+
}
|
|
993
|
+
}, [events]);
|
|
994
|
+
const handleScroll = useCallback2(() => {
|
|
995
|
+
const el = scrollRef.current;
|
|
996
|
+
if (el) {
|
|
997
|
+
isAtBottom.current = el.scrollHeight - el.scrollTop - el.clientHeight < 24;
|
|
998
|
+
}
|
|
999
|
+
}, []);
|
|
1000
|
+
const formatTime = (d) => d.toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
1001
|
+
return /* @__PURE__ */ jsx15(
|
|
1002
|
+
"div",
|
|
1003
|
+
{
|
|
1004
|
+
ref: scrollRef,
|
|
1005
|
+
onScroll: handleScroll,
|
|
1006
|
+
className: cn("overflow-y-auto overflow-x-hidden font-mono text-xs leading-5 scrollbar-thin", className),
|
|
1007
|
+
style: { height },
|
|
1008
|
+
children: /* @__PURE__ */ jsx15(AnimatePresence, { initial: false, children: events.map((event) => /* @__PURE__ */ jsxs12(
|
|
1009
|
+
motion2.div,
|
|
1010
|
+
{
|
|
1011
|
+
initial: { opacity: 0, height: 0 },
|
|
1012
|
+
animate: { opacity: 1, height: "auto" },
|
|
1013
|
+
exit: { opacity: 0, height: 0 },
|
|
1014
|
+
transition: { duration: 0.25, ease: [0.22, 1, 0.36, 1] },
|
|
1015
|
+
className: "flex gap-2 px-2 py-0.5 hover:bg-[var(--ds-gray-alpha-100)]",
|
|
1016
|
+
children: [
|
|
1017
|
+
/* @__PURE__ */ jsx15(
|
|
1018
|
+
"span",
|
|
1019
|
+
{
|
|
1020
|
+
className: "inline-block w-1.5 h-1.5 rounded-full mt-1.5 shrink-0",
|
|
1021
|
+
style: { backgroundColor: typeColors[event.type] }
|
|
1022
|
+
}
|
|
1023
|
+
),
|
|
1024
|
+
showTimestamps && event.timestamp && /* @__PURE__ */ jsx15("span", { className: "shrink-0 opacity-40 tabular-nums", children: formatTime(event.timestamp) }),
|
|
1025
|
+
/* @__PURE__ */ jsx15("span", { className: "opacity-80 break-words min-w-0", children: event.message })
|
|
1026
|
+
]
|
|
1027
|
+
},
|
|
1028
|
+
event.id
|
|
1029
|
+
)) })
|
|
1030
|
+
}
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// src/components/mesh-gradient.tsx
|
|
1035
|
+
import { lazy, Suspense, useEffect as useEffect3, useRef as useRef3, useState as useState5 } from "react";
|
|
1036
|
+
|
|
1037
|
+
// src/hooks/useReducedMotion.ts
|
|
1038
|
+
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
1039
|
+
function useReducedMotion() {
|
|
1040
|
+
const [reduced, setReduced] = useState4(false);
|
|
1041
|
+
useEffect2(() => {
|
|
1042
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
1043
|
+
setReduced(mq.matches || document.documentElement.dataset.reduceMotion === "true");
|
|
1044
|
+
const handler = (e) => setReduced(e.matches);
|
|
1045
|
+
mq.addEventListener("change", handler);
|
|
1046
|
+
return () => mq.removeEventListener("change", handler);
|
|
1047
|
+
}, []);
|
|
1048
|
+
return reduced;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/components/mesh-gradient.tsx
|
|
1052
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
1053
|
+
var MeshGradientShader = lazy(() => import("@paper-design/shaders-react").then((m) => ({ default: m.MeshGradient })));
|
|
1054
|
+
function StaticFallback({ colors }) {
|
|
1055
|
+
const bg = colors.length >= 2 ? `linear-gradient(135deg, ${colors[0]} 0%, ${colors[Math.floor(colors.length / 2)]} 50%, ${colors[colors.length - 1]} 100%)` : colors[0] ?? "#0A0A0A";
|
|
1056
|
+
return /* @__PURE__ */ jsx16("div", { style: { width: "100%", height: "100%", background: bg } });
|
|
1057
|
+
}
|
|
1058
|
+
function MeshGradientInner({ colors, speed = 0.02, className, style }) {
|
|
1059
|
+
const ref = useRef3(null);
|
|
1060
|
+
const [visible, setVisible] = useState5(false);
|
|
1061
|
+
const reducedMotion = useReducedMotion();
|
|
1062
|
+
useEffect3(() => {
|
|
1063
|
+
const el = ref.current;
|
|
1064
|
+
if (!el) return;
|
|
1065
|
+
const observer = new IntersectionObserver(([entry]) => setVisible(entry.isIntersecting), {
|
|
1066
|
+
rootMargin: "100px"
|
|
1067
|
+
});
|
|
1068
|
+
observer.observe(el);
|
|
1069
|
+
return () => observer.disconnect();
|
|
1070
|
+
}, []);
|
|
1071
|
+
return /* @__PURE__ */ jsx16("div", { ref, className, style: { position: "absolute", inset: 0, ...style }, children: visible && !reducedMotion ? /* @__PURE__ */ jsx16(Suspense, { fallback: /* @__PURE__ */ jsx16(StaticFallback, { colors }), children: /* @__PURE__ */ jsx16(MeshGradientShader, { colors, speed, style: { width: "100%", height: "100%" } }) }) : /* @__PURE__ */ jsx16(StaticFallback, { colors }) });
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/components/metric-display.tsx
|
|
1075
|
+
import { cva as cva5 } from "class-variance-authority";
|
|
1076
|
+
import * as React9 from "react";
|
|
1077
|
+
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1078
|
+
var metricDisplayVariants = cva5("flex flex-col", {
|
|
1079
|
+
variants: {
|
|
1080
|
+
size: {
|
|
1081
|
+
/** Compact — for inline/tile use (architecture mockup metric tiles) */
|
|
1082
|
+
sm: "gap-1",
|
|
1083
|
+
/** Standard — for dashboard displays */
|
|
1084
|
+
md: "gap-1.5",
|
|
1085
|
+
/** Large — hero stat cards (metrics.tsx) */
|
|
1086
|
+
lg: "gap-2"
|
|
1087
|
+
}
|
|
1088
|
+
},
|
|
1089
|
+
defaultVariants: {
|
|
1090
|
+
size: "md"
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
var valueSizeMap = {
|
|
1094
|
+
sm: "text-[22px] font-semibold leading-7 tracking-tight",
|
|
1095
|
+
md: "text-[28px] font-semibold leading-8 tracking-[-0.02em]",
|
|
1096
|
+
lg: "text-[36px] sm:text-[44px] font-semibold leading-none tracking-[-0.04em]"
|
|
1097
|
+
};
|
|
1098
|
+
var labelSizeMap = {
|
|
1099
|
+
sm: "text-[11px] uppercase tracking-[0.06em] text-[#FFFFFF80] font-medium leading-3.5",
|
|
1100
|
+
md: "text-[13px] text-[#FFFFFFCC] font-medium leading-4",
|
|
1101
|
+
lg: "text-[15px] text-[#FFFFFFCC] font-medium leading-5"
|
|
1102
|
+
};
|
|
1103
|
+
var MetricDisplay = React9.forwardRef(
|
|
1104
|
+
({ className, size = "md", value, label, description, prefix, suffix, accentColor, ...props }, ref) => {
|
|
1105
|
+
const resolvedSize = size ?? "md";
|
|
1106
|
+
return /* @__PURE__ */ jsxs13("div", { ref, className: cn(metricDisplayVariants({ size }), className), ...props, children: [
|
|
1107
|
+
resolvedSize === "sm" && /* @__PURE__ */ jsx17("span", { className: labelSizeMap[resolvedSize], children: label }),
|
|
1108
|
+
/* @__PURE__ */ jsxs13(
|
|
1109
|
+
"div",
|
|
1110
|
+
{
|
|
1111
|
+
className: cn(valueSizeMap[resolvedSize], "tabular-nums"),
|
|
1112
|
+
style: accentColor ? { color: accentColor } : void 0,
|
|
1113
|
+
children: [
|
|
1114
|
+
prefix && /* @__PURE__ */ jsx17("span", { children: prefix }),
|
|
1115
|
+
/* @__PURE__ */ jsx17("span", { children: value }),
|
|
1116
|
+
suffix && /* @__PURE__ */ jsx17("span", { className: resolvedSize === "lg" ? "text-[32px] tracking-[-0.02em] ml-0.5" : "ml-0.5", children: suffix })
|
|
1117
|
+
]
|
|
1118
|
+
}
|
|
1119
|
+
),
|
|
1120
|
+
resolvedSize !== "sm" && /* @__PURE__ */ jsx17("span", { className: labelSizeMap[resolvedSize], children: label }),
|
|
1121
|
+
description && /* @__PURE__ */ jsx17("span", { className: "text-[13px] text-[#FFFFFF80] leading-4", children: description })
|
|
1122
|
+
] });
|
|
1123
|
+
}
|
|
1124
|
+
);
|
|
1125
|
+
MetricDisplay.displayName = "MetricDisplay";
|
|
1126
|
+
|
|
1127
|
+
// src/components/note.tsx
|
|
1128
|
+
import { cva as cva6 } from "class-variance-authority";
|
|
1129
|
+
import { AlertCircle, AlertTriangle, CheckCircle, Info } from "lucide-react";
|
|
1130
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1131
|
+
var noteVariants = cva6("flex items-start gap-2 rounded-md border p-3 text-copy-13", {
|
|
1132
|
+
variants: {
|
|
1133
|
+
type: {
|
|
1134
|
+
default: "border-gray-alpha-400 bg-gray-alpha-100 text-gray-900",
|
|
1135
|
+
error: "border-red-300 bg-red-100 text-red-900",
|
|
1136
|
+
warning: "border-amber-300 bg-amber-100 text-amber-900",
|
|
1137
|
+
success: "border-green-300 bg-green-100 text-green-900"
|
|
1138
|
+
},
|
|
1139
|
+
size: {
|
|
1140
|
+
default: "p-3",
|
|
1141
|
+
small: "p-2 text-label-12"
|
|
1142
|
+
}
|
|
1143
|
+
},
|
|
1144
|
+
defaultVariants: {
|
|
1145
|
+
type: "default",
|
|
1146
|
+
size: "default"
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
var iconMap = {
|
|
1150
|
+
default: Info,
|
|
1151
|
+
error: AlertCircle,
|
|
1152
|
+
warning: AlertTriangle,
|
|
1153
|
+
success: CheckCircle
|
|
1154
|
+
};
|
|
1155
|
+
function Note({ className, type = "default", size, label, action, children, ...props }) {
|
|
1156
|
+
const Icon2 = iconMap[type];
|
|
1157
|
+
const showLabel = label !== false;
|
|
1158
|
+
return /* @__PURE__ */ jsxs14("div", { className: cn(noteVariants({ type, size }), className), role: "alert", ...props, children: [
|
|
1159
|
+
showLabel && /* @__PURE__ */ jsx18(Icon2, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
1160
|
+
/* @__PURE__ */ jsx18("div", { className: "flex-1 min-w-0", children }),
|
|
1161
|
+
action && /* @__PURE__ */ jsx18("div", { className: "shrink-0", children: action })
|
|
1162
|
+
] });
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// src/components/pill-badge.tsx
|
|
1166
|
+
import { cva as cva7 } from "class-variance-authority";
|
|
1167
|
+
import * as React10 from "react";
|
|
1168
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1169
|
+
var pillBadgeVariants = cva7(
|
|
1170
|
+
"inline-flex items-center gap-1.5 rounded-full font-medium uppercase leading-none whitespace-nowrap",
|
|
1171
|
+
{
|
|
1172
|
+
variants: {
|
|
1173
|
+
variant: {
|
|
1174
|
+
/** Subtle border badge — compliance tags, capability tags */
|
|
1175
|
+
default: "border border-[#FFFFFF26] bg-[#FFFFFF08] text-[#FFFFFFCC]",
|
|
1176
|
+
/** Muted badge — less prominent */
|
|
1177
|
+
muted: "border border-[#FFFFFF14] bg-[#FFFFFF05] text-[#FFFFFFCC]",
|
|
1178
|
+
/** Accent-filled badge — ROI labels, active state badges */
|
|
1179
|
+
accent: "text-[var(--color-accent,#D49355)] bg-[rgba(var(--color-accent-rgb,212,147,85),0.12)]"
|
|
1180
|
+
},
|
|
1181
|
+
size: {
|
|
1182
|
+
sm: "py-1 px-2.5 text-[10px] tracking-[0.08em]",
|
|
1183
|
+
md: "py-1.5 px-3.5 text-[11px] tracking-widest",
|
|
1184
|
+
lg: "py-2 px-4 text-[11px] tracking-widest"
|
|
1185
|
+
}
|
|
1186
|
+
},
|
|
1187
|
+
defaultVariants: {
|
|
1188
|
+
variant: "default",
|
|
1189
|
+
size: "md"
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
var PillBadge = React10.forwardRef(
|
|
1194
|
+
({ className, variant, size, dot, dotColor, children, ...props }, ref) => {
|
|
1195
|
+
return /* @__PURE__ */ jsxs15("span", { ref, className: cn(pillBadgeVariants({ variant, size }), className), ...props, children: [
|
|
1196
|
+
dot && /* @__PURE__ */ jsx19("span", { className: "size-1.5 shrink-0 rounded-full", style: { backgroundColor: dotColor || "#FFFFFF40" } }),
|
|
1197
|
+
children
|
|
1198
|
+
] });
|
|
1199
|
+
}
|
|
1200
|
+
);
|
|
1201
|
+
PillBadge.displayName = "PillBadge";
|
|
1202
|
+
|
|
1203
|
+
// src/components/progress-bar.tsx
|
|
1204
|
+
import { cva as cva8 } from "class-variance-authority";
|
|
1205
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1206
|
+
var progressBarVariants = cva8(
|
|
1207
|
+
"relative w-full overflow-hidden rounded-full [background:var(--khal-border-default)]",
|
|
1208
|
+
{
|
|
1209
|
+
variants: {
|
|
1210
|
+
size: {
|
|
1211
|
+
sm: "h-1.5",
|
|
1212
|
+
md: "h-2.5"
|
|
1213
|
+
}
|
|
1214
|
+
},
|
|
1215
|
+
defaultVariants: {
|
|
1216
|
+
size: "md"
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
);
|
|
1220
|
+
function ProgressBar({
|
|
1221
|
+
value,
|
|
1222
|
+
max = 100,
|
|
1223
|
+
color = "var(--khal-stage-build)",
|
|
1224
|
+
size,
|
|
1225
|
+
showLabel = false,
|
|
1226
|
+
className,
|
|
1227
|
+
...props
|
|
1228
|
+
}) {
|
|
1229
|
+
const percentage = Math.min(100, Math.max(0, value / max * 100));
|
|
1230
|
+
return /* @__PURE__ */ jsxs16("div", { className: cn("flex items-center gap-2", className), ...props, children: [
|
|
1231
|
+
/* @__PURE__ */ jsx20(
|
|
1232
|
+
"div",
|
|
1233
|
+
{
|
|
1234
|
+
role: "progressbar",
|
|
1235
|
+
"aria-valuenow": value,
|
|
1236
|
+
"aria-valuemin": 0,
|
|
1237
|
+
"aria-valuemax": max,
|
|
1238
|
+
className: progressBarVariants({ size }),
|
|
1239
|
+
children: /* @__PURE__ */ jsx20(
|
|
1240
|
+
"div",
|
|
1241
|
+
{
|
|
1242
|
+
className: "h-full rounded-full",
|
|
1243
|
+
style: {
|
|
1244
|
+
width: `${percentage}%`,
|
|
1245
|
+
background: `linear-gradient(90deg, color-mix(in srgb, ${color} 85%, black), ${color})`,
|
|
1246
|
+
transition: "width var(--khal-duration-normal) var(--khal-ease-spring)"
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
)
|
|
1250
|
+
}
|
|
1251
|
+
),
|
|
1252
|
+
showLabel && /* @__PURE__ */ jsxs16("span", { className: "shrink-0 text-xs tabular-nums opacity-60", children: [
|
|
1253
|
+
value,
|
|
1254
|
+
"/",
|
|
1255
|
+
max
|
|
1256
|
+
] })
|
|
1257
|
+
] });
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// src/components/section-card.tsx
|
|
1261
|
+
import { cva as cva9 } from "class-variance-authority";
|
|
1262
|
+
import * as React11 from "react";
|
|
1263
|
+
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1264
|
+
var sectionCardVariants = cva9("relative flex flex-col overflow-hidden", {
|
|
1265
|
+
variants: {
|
|
1266
|
+
variant: {
|
|
1267
|
+
default: "rounded-2xl border border-[#FFFFFF1A] bg-[#FFFFFF0A]",
|
|
1268
|
+
inset: "rounded-tl-xl border-t border-l border-[#FFFFFF26] bg-[#111111]",
|
|
1269
|
+
solid: "rounded-2xl border border-[#FFFFFF1A] bg-[#0D0D0D]"
|
|
1270
|
+
},
|
|
1271
|
+
padding: {
|
|
1272
|
+
none: "",
|
|
1273
|
+
sm: "p-4",
|
|
1274
|
+
md: "p-5 sm:p-6",
|
|
1275
|
+
lg: "p-5 sm:p-6 md:p-8"
|
|
1276
|
+
}
|
|
1277
|
+
},
|
|
1278
|
+
defaultVariants: {
|
|
1279
|
+
variant: "default",
|
|
1280
|
+
padding: "md"
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
var SectionCard = React11.forwardRef(
|
|
1284
|
+
({ className, variant, padding, glow, children, ...props }, ref) => {
|
|
1285
|
+
return /* @__PURE__ */ jsxs17("div", { ref, className: cn(sectionCardVariants({ variant, padding }), className), ...props, children: [
|
|
1286
|
+
glow && /* @__PURE__ */ jsx21(
|
|
1287
|
+
"div",
|
|
1288
|
+
{
|
|
1289
|
+
className: "pointer-events-none absolute inset-0 z-0",
|
|
1290
|
+
style: {
|
|
1291
|
+
background: `linear-gradient(180deg, ${glow}22 0%, transparent 60%)`
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
),
|
|
1295
|
+
/* @__PURE__ */ jsx21("div", { className: "relative z-10 flex flex-col h-full", children })
|
|
1296
|
+
] });
|
|
1297
|
+
}
|
|
1298
|
+
);
|
|
1299
|
+
SectionCard.displayName = "SectionCard";
|
|
1300
|
+
function SectionCardHeader({ className, children, ...props }) {
|
|
1301
|
+
return /* @__PURE__ */ jsx21(
|
|
1302
|
+
"div",
|
|
1303
|
+
{
|
|
1304
|
+
className: cn("flex items-center justify-between py-3 px-4 border-b border-[#FFFFFF1A]", className),
|
|
1305
|
+
...props,
|
|
1306
|
+
children
|
|
1307
|
+
}
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
SectionCardHeader.displayName = "SectionCardHeader";
|
|
1311
|
+
|
|
1312
|
+
// src/components/separator.tsx
|
|
1313
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
1314
|
+
import * as React12 from "react";
|
|
1315
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1316
|
+
var Separator3 = React12.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx22(
|
|
1317
|
+
SeparatorPrimitive.Root,
|
|
1318
|
+
{
|
|
1319
|
+
ref,
|
|
1320
|
+
decorative,
|
|
1321
|
+
orientation,
|
|
1322
|
+
className: cn(
|
|
1323
|
+
"shrink-0 bg-gray-alpha-400",
|
|
1324
|
+
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
|
1325
|
+
className
|
|
1326
|
+
),
|
|
1327
|
+
...props
|
|
1328
|
+
}
|
|
1329
|
+
));
|
|
1330
|
+
Separator3.displayName = SeparatorPrimitive.Root.displayName;
|
|
1331
|
+
|
|
1332
|
+
// src/components/switch.tsx
|
|
1333
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
1334
|
+
import * as React13 from "react";
|
|
1335
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
1336
|
+
var Toggle = React13.forwardRef(
|
|
1337
|
+
({ className, onChange, onCheckedChange, ...props }, ref) => /* @__PURE__ */ jsx23(
|
|
1338
|
+
SwitchPrimitive.Root,
|
|
1339
|
+
{
|
|
1340
|
+
className: cn(
|
|
1341
|
+
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors",
|
|
1342
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-700 focus-visible:ring-offset-2 focus-visible:ring-offset-background-100",
|
|
1343
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1344
|
+
"data-[state=checked]:bg-gray-1000 data-[state=unchecked]:bg-gray-alpha-400",
|
|
1345
|
+
className
|
|
1346
|
+
),
|
|
1347
|
+
onCheckedChange: onCheckedChange ?? onChange,
|
|
1348
|
+
...props,
|
|
1349
|
+
ref,
|
|
1350
|
+
children: /* @__PURE__ */ jsx23(
|
|
1351
|
+
SwitchPrimitive.Thumb,
|
|
1352
|
+
{
|
|
1353
|
+
className: cn(
|
|
1354
|
+
"pointer-events-none block h-4 w-4 rounded-full bg-white shadow-lg ring-0 transition-transform",
|
|
1355
|
+
"data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
|
1356
|
+
)
|
|
1357
|
+
}
|
|
1358
|
+
)
|
|
1359
|
+
}
|
|
1360
|
+
)
|
|
1361
|
+
);
|
|
1362
|
+
Toggle.displayName = "Toggle";
|
|
1363
|
+
|
|
1364
|
+
// src/components/theme-provider.tsx
|
|
1365
|
+
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
|
1366
|
+
import { useEffect as useEffect4 } from "react";
|
|
1367
|
+
|
|
1368
|
+
// src/stores/theme-store.ts
|
|
1369
|
+
import { create } from "zustand";
|
|
1370
|
+
import { persist } from "zustand/middleware";
|
|
1371
|
+
var useThemeStore = create()(
|
|
1372
|
+
persist(
|
|
1373
|
+
(set) => ({
|
|
1374
|
+
mode: "dark",
|
|
1375
|
+
setMode: (mode) => set({ mode }),
|
|
1376
|
+
reduceMotion: false,
|
|
1377
|
+
setReduceMotion: (reduceMotion) => set({ reduceMotion }),
|
|
1378
|
+
glassEnabled: false,
|
|
1379
|
+
setGlassEnabled: (glassEnabled) => set({ glassEnabled }),
|
|
1380
|
+
gpuTerminals: false,
|
|
1381
|
+
setGpuTerminals: (gpuTerminals) => set({ gpuTerminals })
|
|
1382
|
+
}),
|
|
1383
|
+
{
|
|
1384
|
+
name: "khal-theme"
|
|
1385
|
+
}
|
|
1386
|
+
)
|
|
1387
|
+
);
|
|
1388
|
+
|
|
1389
|
+
// src/components/theme-provider.tsx
|
|
1390
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1391
|
+
function ReduceMotionSync() {
|
|
1392
|
+
const reduceMotion = useThemeStore((s) => s.reduceMotion);
|
|
1393
|
+
useEffect4(() => {
|
|
1394
|
+
document.documentElement.setAttribute("data-reduce-motion", String(reduceMotion));
|
|
1395
|
+
}, [reduceMotion]);
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
function GlassSync() {
|
|
1399
|
+
const glassEnabled = useThemeStore((s) => s.glassEnabled);
|
|
1400
|
+
useEffect4(() => {
|
|
1401
|
+
const el = document.documentElement;
|
|
1402
|
+
if (glassEnabled) {
|
|
1403
|
+
el.setAttribute("data-glass", "");
|
|
1404
|
+
el.style.setProperty("--khal-glass-enabled", "1");
|
|
1405
|
+
} else {
|
|
1406
|
+
el.removeAttribute("data-glass");
|
|
1407
|
+
el.style.setProperty("--khal-glass-enabled", "0");
|
|
1408
|
+
}
|
|
1409
|
+
}, [glassEnabled]);
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
function GpuTerminalsSync() {
|
|
1413
|
+
const gpuTerminals = useThemeStore((s) => s.gpuTerminals);
|
|
1414
|
+
useEffect4(() => {
|
|
1415
|
+
if (gpuTerminals) {
|
|
1416
|
+
localStorage.setItem("khal-gpu-terminals", "true");
|
|
1417
|
+
} else {
|
|
1418
|
+
localStorage.removeItem("khal-gpu-terminals");
|
|
1419
|
+
}
|
|
1420
|
+
}, [gpuTerminals]);
|
|
1421
|
+
return null;
|
|
1422
|
+
}
|
|
1423
|
+
function ThemeProvider({ children, ...props }) {
|
|
1424
|
+
return /* @__PURE__ */ jsxs18(NextThemesProvider, { ...props, children: [
|
|
1425
|
+
/* @__PURE__ */ jsx24(ReduceMotionSync, {}),
|
|
1426
|
+
/* @__PURE__ */ jsx24(GlassSync, {}),
|
|
1427
|
+
/* @__PURE__ */ jsx24(GpuTerminalsSync, {}),
|
|
1428
|
+
children
|
|
1429
|
+
] });
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// src/components/theme-switcher.tsx
|
|
1433
|
+
import { Monitor, Moon, Sun } from "lucide-react";
|
|
1434
|
+
import { useTheme } from "next-themes";
|
|
1435
|
+
import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
1436
|
+
import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1437
|
+
var themes = [
|
|
1438
|
+
{ value: "system", label: "System", icon: Monitor },
|
|
1439
|
+
{ value: "light", label: "Light", icon: Sun },
|
|
1440
|
+
{ value: "dark", label: "Dark", icon: Moon }
|
|
1441
|
+
];
|
|
1442
|
+
function ThemeSwitcher({ small, className, onThemeSwitch, disabled }) {
|
|
1443
|
+
const { theme, setTheme } = useTheme();
|
|
1444
|
+
const [mounted, setMounted] = useState6(false);
|
|
1445
|
+
useEffect5(() => setMounted(true), []);
|
|
1446
|
+
const resolvedTheme = mounted ? theme : void 0;
|
|
1447
|
+
return /* @__PURE__ */ jsx25(
|
|
1448
|
+
"fieldset",
|
|
1449
|
+
{
|
|
1450
|
+
className: cn("inline-flex items-center gap-1 rounded-lg bg-gray-alpha-100 p-1", className),
|
|
1451
|
+
disabled,
|
|
1452
|
+
children: themes.map(({ value, label, icon: Icon2 }) => /* @__PURE__ */ jsxs19(
|
|
1453
|
+
"button",
|
|
1454
|
+
{
|
|
1455
|
+
type: "button",
|
|
1456
|
+
role: "radio",
|
|
1457
|
+
"aria-checked": resolvedTheme === value,
|
|
1458
|
+
onClick: () => {
|
|
1459
|
+
setTheme(value);
|
|
1460
|
+
onThemeSwitch?.(value);
|
|
1461
|
+
},
|
|
1462
|
+
className: cn(
|
|
1463
|
+
"inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-label-12 transition-colors cursor-pointer",
|
|
1464
|
+
resolvedTheme === value ? "bg-background-100 text-gray-1000 shadow-sm" : "text-gray-700 hover:text-gray-1000"
|
|
1465
|
+
),
|
|
1466
|
+
children: [
|
|
1467
|
+
/* @__PURE__ */ jsx25(Icon2, { className: small ? "h-3.5 w-3.5" : "h-4 w-4" }),
|
|
1468
|
+
!small && label
|
|
1469
|
+
]
|
|
1470
|
+
},
|
|
1471
|
+
value
|
|
1472
|
+
))
|
|
1473
|
+
}
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/components/ticker-bar.tsx
|
|
1478
|
+
import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1479
|
+
function TickerBar({ children, duration = 30, pauseOnHover = true, className }) {
|
|
1480
|
+
return /* @__PURE__ */ jsx26(
|
|
1481
|
+
"div",
|
|
1482
|
+
{
|
|
1483
|
+
className: cn("relative w-full overflow-hidden", className),
|
|
1484
|
+
style: {
|
|
1485
|
+
maskImage: "linear-gradient(to right, transparent, black 10%, black 90%, transparent)",
|
|
1486
|
+
WebkitMaskImage: "linear-gradient(to right, transparent, black 10%, black 90%, transparent)"
|
|
1487
|
+
},
|
|
1488
|
+
children: /* @__PURE__ */ jsxs20(
|
|
1489
|
+
"div",
|
|
1490
|
+
{
|
|
1491
|
+
className: cn("flex w-max", pauseOnHover && "hover:[animation-play-state:paused]"),
|
|
1492
|
+
style: {
|
|
1493
|
+
animation: `khal-ticker ${duration}s linear infinite`
|
|
1494
|
+
},
|
|
1495
|
+
children: [
|
|
1496
|
+
/* @__PURE__ */ jsx26("div", { className: "flex shrink-0 items-center", children }),
|
|
1497
|
+
/* @__PURE__ */ jsx26("div", { className: "flex shrink-0 items-center", "aria-hidden": true, children })
|
|
1498
|
+
]
|
|
1499
|
+
}
|
|
1500
|
+
)
|
|
1501
|
+
}
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// src/components/tooltip.tsx
|
|
1506
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
1507
|
+
import * as React14 from "react";
|
|
1508
|
+
import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1509
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
1510
|
+
var TooltipRoot = TooltipPrimitive.Root;
|
|
1511
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
1512
|
+
var TooltipContent = React14.forwardRef(({ className, sideOffset = 4, style, ...props }, ref) => /* @__PURE__ */ jsx27(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx27(
|
|
1513
|
+
TooltipPrimitive.Content,
|
|
1514
|
+
{
|
|
1515
|
+
ref,
|
|
1516
|
+
sideOffset,
|
|
1517
|
+
className: cn(
|
|
1518
|
+
"z-[9999] overflow-hidden rounded-md px-2.5 py-1 text-label-12",
|
|
1519
|
+
"animate-in fade-in-0 zoom-in-95",
|
|
1520
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
1521
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
1522
|
+
className
|
|
1523
|
+
),
|
|
1524
|
+
style: {
|
|
1525
|
+
background: "var(--khal-text-primary, var(--ds-gray-1000))",
|
|
1526
|
+
color: "var(--khal-text-inverse, var(--ds-background-100))",
|
|
1527
|
+
...style
|
|
1528
|
+
},
|
|
1529
|
+
...props
|
|
1530
|
+
}
|
|
1531
|
+
) }));
|
|
1532
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
1533
|
+
function Tooltip({ text, children, position = "top", delay, delayTime, desktopOnly, className }) {
|
|
1534
|
+
const delayDuration = delayTime ?? (delay ? 400 : 200);
|
|
1535
|
+
return /* @__PURE__ */ jsx27(TooltipProvider, { delayDuration, children: /* @__PURE__ */ jsxs21(TooltipRoot, { children: [
|
|
1536
|
+
/* @__PURE__ */ jsx27(TooltipTrigger, { asChild: true, children }),
|
|
1537
|
+
/* @__PURE__ */ jsx27(TooltipContent, { side: position, className: cn(desktopOnly && "max-md:hidden", className), children: text })
|
|
1538
|
+
] }) });
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// src/components/window-minimized-context.tsx
|
|
1542
|
+
import { createContext, useContext } from "react";
|
|
1543
|
+
var WindowMinimizedContext = createContext(false);
|
|
1544
|
+
var WindowMinimizedProvider = WindowMinimizedContext.Provider;
|
|
1545
|
+
function useWindowMinimized() {
|
|
1546
|
+
return useContext(WindowMinimizedContext);
|
|
1547
|
+
}
|
|
1548
|
+
var WindowActiveContext = createContext(true);
|
|
1549
|
+
var WindowActiveProvider = WindowActiveContext.Provider;
|
|
1550
|
+
function useWindowActive() {
|
|
1551
|
+
return useContext(WindowActiveContext);
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// src/lib/animations.ts
|
|
1555
|
+
var khalEasing = [0.22, 1, 0.36, 1];
|
|
1556
|
+
var springConfig = {
|
|
1557
|
+
stiffness: 300,
|
|
1558
|
+
damping: 22
|
|
1559
|
+
};
|
|
1560
|
+
var fadeUp = {
|
|
1561
|
+
initial: { opacity: 0, y: 12, filter: "blur(4px)" },
|
|
1562
|
+
animate: { opacity: 1, y: 0, filter: "blur(0px)" },
|
|
1563
|
+
transition: { duration: 0.7, ease: khalEasing }
|
|
1564
|
+
};
|
|
1565
|
+
var scaleUp = {
|
|
1566
|
+
initial: { opacity: 0, scale: 0.96, filter: "blur(6px)" },
|
|
1567
|
+
animate: { opacity: 1, scale: 1, filter: "blur(0px)" },
|
|
1568
|
+
transition: { duration: 0.9, ease: khalEasing }
|
|
1569
|
+
};
|
|
1570
|
+
var staggerContainer = {
|
|
1571
|
+
animate: {
|
|
1572
|
+
transition: {
|
|
1573
|
+
staggerChildren: 0.12
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
var staggerChild = {
|
|
1578
|
+
initial: { opacity: 0, y: 8 },
|
|
1579
|
+
animate: { opacity: 1, y: 0 },
|
|
1580
|
+
transition: { duration: 0.4, ease: khalEasing }
|
|
1581
|
+
};
|
|
1582
|
+
var fadeIn = {
|
|
1583
|
+
initial: { opacity: 0 },
|
|
1584
|
+
animate: { opacity: 1 },
|
|
1585
|
+
transition: { duration: 0.5, ease: khalEasing }
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
// src/primitives/collapsible-sidebar.tsx
|
|
1589
|
+
import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useRef as useRef4, useState as useState7 } from "react";
|
|
1590
|
+
import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1591
|
+
var SidebarContext = createContext2({
|
|
1592
|
+
collapsed: false,
|
|
1593
|
+
toggle: () => {
|
|
1594
|
+
},
|
|
1595
|
+
size: 220
|
|
1596
|
+
});
|
|
1597
|
+
function useSidebar() {
|
|
1598
|
+
return useContext2(SidebarContext);
|
|
1599
|
+
}
|
|
1600
|
+
function CollapsibleSidebarRoot({
|
|
1601
|
+
children,
|
|
1602
|
+
defaultSize = 220,
|
|
1603
|
+
min = 140,
|
|
1604
|
+
max = 400,
|
|
1605
|
+
defaultCollapsed = false,
|
|
1606
|
+
side = "left",
|
|
1607
|
+
className = ""
|
|
1608
|
+
}) {
|
|
1609
|
+
const [collapsed, setCollapsed] = useState7(defaultCollapsed);
|
|
1610
|
+
const [size, setSize] = useState7(defaultSize);
|
|
1611
|
+
const dragging = useRef4(false);
|
|
1612
|
+
const startX = useRef4(0);
|
|
1613
|
+
const startSize = useRef4(0);
|
|
1614
|
+
const toggle = useCallback3(() => setCollapsed((v) => !v), []);
|
|
1615
|
+
const onPointerDown = useCallback3(
|
|
1616
|
+
(e) => {
|
|
1617
|
+
if (collapsed) return;
|
|
1618
|
+
e.preventDefault();
|
|
1619
|
+
dragging.current = true;
|
|
1620
|
+
startX.current = e.clientX;
|
|
1621
|
+
startSize.current = size;
|
|
1622
|
+
e.target.setPointerCapture(e.pointerId);
|
|
1623
|
+
},
|
|
1624
|
+
[collapsed, size]
|
|
1625
|
+
);
|
|
1626
|
+
const onPointerMove = useCallback3(
|
|
1627
|
+
(e) => {
|
|
1628
|
+
if (!dragging.current) return;
|
|
1629
|
+
const delta = side === "left" ? e.clientX - startX.current : startX.current - e.clientX;
|
|
1630
|
+
setSize(Math.min(max, Math.max(min, startSize.current + delta)));
|
|
1631
|
+
},
|
|
1632
|
+
[side, min, max]
|
|
1633
|
+
);
|
|
1634
|
+
const onPointerUp = useCallback3(() => {
|
|
1635
|
+
dragging.current = false;
|
|
1636
|
+
}, []);
|
|
1637
|
+
const resizeHandle = /* @__PURE__ */ jsx28(
|
|
1638
|
+
"div",
|
|
1639
|
+
{
|
|
1640
|
+
className: `w-px shrink-0 cursor-col-resize bg-gray-alpha-200 transition-colors hover:w-0.5 hover:bg-blue-700/50 active:w-0.5 active:bg-blue-700 ${collapsed ? "pointer-events-none" : ""}`,
|
|
1641
|
+
onPointerDown,
|
|
1642
|
+
onPointerMove,
|
|
1643
|
+
onPointerUp,
|
|
1644
|
+
role: "separator",
|
|
1645
|
+
"aria-orientation": "vertical"
|
|
1646
|
+
}
|
|
1647
|
+
);
|
|
1648
|
+
return /* @__PURE__ */ jsx28(SidebarContext.Provider, { value: { collapsed, toggle, size }, children: /* @__PURE__ */ jsxs22("div", { className: `flex shrink-0 ${className}`, style: { width: collapsed ? 0 : size }, children: [
|
|
1649
|
+
side === "right" && resizeHandle,
|
|
1650
|
+
/* @__PURE__ */ jsx28(
|
|
1651
|
+
"div",
|
|
1652
|
+
{
|
|
1653
|
+
className: `flex h-full flex-col overflow-hidden transition-[width] duration-150 ${collapsed ? "w-0" : "w-full"}`,
|
|
1654
|
+
children
|
|
1655
|
+
}
|
|
1656
|
+
),
|
|
1657
|
+
side === "left" && resizeHandle
|
|
1658
|
+
] }) });
|
|
1659
|
+
}
|
|
1660
|
+
function SidebarHeader({ children, className = "" }) {
|
|
1661
|
+
return /* @__PURE__ */ jsx28("div", { className: `flex h-9 shrink-0 items-center justify-between border-b border-gray-alpha-200 px-3 ${className}`, children });
|
|
1662
|
+
}
|
|
1663
|
+
function CollapseButton({ className = "" }) {
|
|
1664
|
+
const { collapsed, toggle } = useSidebar();
|
|
1665
|
+
return /* @__PURE__ */ jsx28(
|
|
1666
|
+
"button",
|
|
1667
|
+
{
|
|
1668
|
+
onClick: toggle,
|
|
1669
|
+
className: `inline-flex h-5 w-5 items-center justify-center rounded text-gray-800 hover:bg-gray-alpha-200 hover:text-gray-1000 transition-colors ${className}`,
|
|
1670
|
+
"aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1671
|
+
children: /* @__PURE__ */ jsx28("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx28(
|
|
1672
|
+
"path",
|
|
1673
|
+
{
|
|
1674
|
+
d: collapsed ? "M4.5 2L8.5 6L4.5 10" : "M8.5 2L4.5 6L8.5 10",
|
|
1675
|
+
stroke: "currentColor",
|
|
1676
|
+
strokeWidth: "1.5",
|
|
1677
|
+
strokeLinecap: "round",
|
|
1678
|
+
strokeLinejoin: "round"
|
|
1679
|
+
}
|
|
1680
|
+
) })
|
|
1681
|
+
}
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
function SidebarContent({ children, className = "" }) {
|
|
1685
|
+
return /* @__PURE__ */ jsx28("div", { className: `flex-1 overflow-y-auto ${className}`, children });
|
|
1686
|
+
}
|
|
1687
|
+
function SidebarSection({
|
|
1688
|
+
title,
|
|
1689
|
+
children,
|
|
1690
|
+
className = ""
|
|
1691
|
+
}) {
|
|
1692
|
+
return /* @__PURE__ */ jsxs22("div", { className: `${className}`, children: [
|
|
1693
|
+
title && /* @__PURE__ */ jsx28("div", { className: "px-3 pt-2 pb-1", children: /* @__PURE__ */ jsx28("span", { className: "text-label-13 font-medium text-gray-800", children: title }) }),
|
|
1694
|
+
children
|
|
1695
|
+
] });
|
|
1696
|
+
}
|
|
1697
|
+
function SidebarItem({ children, icon, active, indent = 0, onClick, className = "" }) {
|
|
1698
|
+
return /* @__PURE__ */ jsxs22(
|
|
1699
|
+
"button",
|
|
1700
|
+
{
|
|
1701
|
+
onClick,
|
|
1702
|
+
className: `flex w-full items-center gap-2 rounded-md px-2 py-1 text-left text-label-13 transition-colors
|
|
1703
|
+
${active ? "bg-gray-alpha-200 text-gray-1000" : "text-gray-900 hover:bg-gray-alpha-100 hover:text-gray-1000"}
|
|
1704
|
+
${className}`,
|
|
1705
|
+
style: { paddingLeft: 8 + indent * 12 },
|
|
1706
|
+
children: [
|
|
1707
|
+
icon && /* @__PURE__ */ jsx28("span", { className: "shrink-0 text-gray-800 [&>svg]:h-3.5 [&>svg]:w-3.5", children: icon }),
|
|
1708
|
+
/* @__PURE__ */ jsx28("span", { className: "min-w-0 truncate", children })
|
|
1709
|
+
]
|
|
1710
|
+
}
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
var CollapsibleSidebar = Object.assign(CollapsibleSidebarRoot, {
|
|
1714
|
+
Header: SidebarHeader,
|
|
1715
|
+
CollapseButton,
|
|
1716
|
+
Content: SidebarContent,
|
|
1717
|
+
Section: SidebarSection,
|
|
1718
|
+
Item: SidebarItem
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
// src/primitives/dialog.tsx
|
|
1722
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
1723
|
+
function DialogRoot({ open, onClose, children }) {
|
|
1724
|
+
if (!open) return null;
|
|
1725
|
+
return /* @__PURE__ */ jsx29("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40", onClick: onClose, children: /* @__PURE__ */ jsx29(
|
|
1726
|
+
"div",
|
|
1727
|
+
{
|
|
1728
|
+
className: "bg-popover text-popover-foreground rounded-lg border p-6 shadow-lg",
|
|
1729
|
+
onClick: (e) => e.stopPropagation(),
|
|
1730
|
+
children
|
|
1731
|
+
}
|
|
1732
|
+
) });
|
|
1733
|
+
}
|
|
1734
|
+
function Body({ children }) {
|
|
1735
|
+
return /* @__PURE__ */ jsx29("div", { className: "flex items-start gap-4", children });
|
|
1736
|
+
}
|
|
1737
|
+
function Icon({ children, variant: _variant }) {
|
|
1738
|
+
return /* @__PURE__ */ jsx29("div", { className: "shrink-0", children });
|
|
1739
|
+
}
|
|
1740
|
+
function Title2({ children }) {
|
|
1741
|
+
return /* @__PURE__ */ jsx29("h2", { className: "text-lg font-semibold", children });
|
|
1742
|
+
}
|
|
1743
|
+
function Description({ children }) {
|
|
1744
|
+
return /* @__PURE__ */ jsx29("p", { className: "text-muted-foreground text-sm", children });
|
|
1745
|
+
}
|
|
1746
|
+
function Actions({ children }) {
|
|
1747
|
+
return /* @__PURE__ */ jsx29("div", { className: "mt-4 flex justify-end gap-2", children });
|
|
1748
|
+
}
|
|
1749
|
+
function Cancel({ children, ...props }) {
|
|
1750
|
+
return /* @__PURE__ */ jsx29("button", { type: "button", className: "rounded px-3 py-1.5 text-sm hover:bg-muted", ...props, children });
|
|
1751
|
+
}
|
|
1752
|
+
function Confirm({ children, variant: _variant, ...props }) {
|
|
1753
|
+
return /* @__PURE__ */ jsx29("button", { type: "button", className: "rounded bg-destructive px-3 py-1.5 text-sm text-destructive-foreground", ...props, children });
|
|
1754
|
+
}
|
|
1755
|
+
var Dialog = Object.assign(DialogRoot, {
|
|
1756
|
+
Body,
|
|
1757
|
+
Icon,
|
|
1758
|
+
Title: Title2,
|
|
1759
|
+
Description,
|
|
1760
|
+
Actions,
|
|
1761
|
+
Cancel,
|
|
1762
|
+
Confirm
|
|
1763
|
+
});
|
|
1764
|
+
|
|
1765
|
+
// src/primitives/empty-state.tsx
|
|
1766
|
+
import { jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1767
|
+
function EmptyState({ icon, title, description, action, compact, className = "" }) {
|
|
1768
|
+
return /* @__PURE__ */ jsxs23(
|
|
1769
|
+
"div",
|
|
1770
|
+
{
|
|
1771
|
+
className: `flex flex-col items-center justify-center text-center ${compact ? "gap-2 py-6" : "gap-3 py-12"} ${className}`,
|
|
1772
|
+
children: [
|
|
1773
|
+
icon && /* @__PURE__ */ jsx30("div", { className: `text-gray-700 ${compact ? "[&>svg]:h-5 [&>svg]:w-5" : "[&>svg]:h-8 [&>svg]:w-8"}`, children: icon }),
|
|
1774
|
+
/* @__PURE__ */ jsxs23("div", { className: "space-y-0.5", children: [
|
|
1775
|
+
/* @__PURE__ */ jsx30("p", { className: "text-label-13 font-medium text-gray-1000", children: title }),
|
|
1776
|
+
description && /* @__PURE__ */ jsx30("p", { className: `text-gray-800 ${compact ? "text-label-13" : "text-label-13"}`, children: description })
|
|
1777
|
+
] }),
|
|
1778
|
+
action && /* @__PURE__ */ jsx30("div", { className: "mt-2 shrink-0", children: action })
|
|
1779
|
+
]
|
|
1780
|
+
}
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// src/primitives/list-view.tsx
|
|
1785
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef5, useState as useState8 } from "react";
|
|
1786
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1787
|
+
function ListView({
|
|
1788
|
+
items,
|
|
1789
|
+
selected,
|
|
1790
|
+
onSelect,
|
|
1791
|
+
onActivate,
|
|
1792
|
+
renderItem,
|
|
1793
|
+
getKey,
|
|
1794
|
+
multiSelect = false,
|
|
1795
|
+
emptyMessage = "No items",
|
|
1796
|
+
className = ""
|
|
1797
|
+
}) {
|
|
1798
|
+
const [focusIndex, setFocusIndex] = useState8(0);
|
|
1799
|
+
const listRef = useRef5(null);
|
|
1800
|
+
const selectedSet = new Set(selected == null ? [] : Array.isArray(selected) ? selected : [selected]);
|
|
1801
|
+
const clamp = useCallback4((i) => Math.max(0, Math.min(items.length - 1, i)), [items.length]);
|
|
1802
|
+
useEffect6(() => {
|
|
1803
|
+
setFocusIndex((prev) => clamp(prev));
|
|
1804
|
+
}, [items.length, clamp]);
|
|
1805
|
+
const scrollToIndex = useCallback4((i) => {
|
|
1806
|
+
const el = listRef.current?.children[i];
|
|
1807
|
+
el?.scrollIntoView({ block: "nearest" });
|
|
1808
|
+
}, []);
|
|
1809
|
+
const handleKeyDown = useCallback4(
|
|
1810
|
+
(e) => {
|
|
1811
|
+
switch (e.key) {
|
|
1812
|
+
case "ArrowDown": {
|
|
1813
|
+
e.preventDefault();
|
|
1814
|
+
const next = clamp(focusIndex + 1);
|
|
1815
|
+
setFocusIndex(next);
|
|
1816
|
+
scrollToIndex(next);
|
|
1817
|
+
if (!multiSelect) onSelect?.(getKey(items[next]));
|
|
1818
|
+
break;
|
|
1819
|
+
}
|
|
1820
|
+
case "ArrowUp": {
|
|
1821
|
+
e.preventDefault();
|
|
1822
|
+
const prev = clamp(focusIndex - 1);
|
|
1823
|
+
setFocusIndex(prev);
|
|
1824
|
+
scrollToIndex(prev);
|
|
1825
|
+
if (!multiSelect) onSelect?.(getKey(items[prev]));
|
|
1826
|
+
break;
|
|
1827
|
+
}
|
|
1828
|
+
case "Home": {
|
|
1829
|
+
e.preventDefault();
|
|
1830
|
+
setFocusIndex(0);
|
|
1831
|
+
scrollToIndex(0);
|
|
1832
|
+
if (!multiSelect && items.length > 0) onSelect?.(getKey(items[0]));
|
|
1833
|
+
break;
|
|
1834
|
+
}
|
|
1835
|
+
case "End": {
|
|
1836
|
+
e.preventDefault();
|
|
1837
|
+
const last = items.length - 1;
|
|
1838
|
+
setFocusIndex(last);
|
|
1839
|
+
scrollToIndex(last);
|
|
1840
|
+
if (!multiSelect && items.length > 0) onSelect?.(getKey(items[last]));
|
|
1841
|
+
break;
|
|
1842
|
+
}
|
|
1843
|
+
case "Enter":
|
|
1844
|
+
case " ": {
|
|
1845
|
+
e.preventDefault();
|
|
1846
|
+
const item = items[focusIndex];
|
|
1847
|
+
if (item) {
|
|
1848
|
+
onSelect?.(getKey(item));
|
|
1849
|
+
if (e.key === "Enter") onActivate?.(item);
|
|
1850
|
+
}
|
|
1851
|
+
break;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
},
|
|
1855
|
+
[focusIndex, items, clamp, scrollToIndex, onSelect, onActivate, getKey, multiSelect]
|
|
1856
|
+
);
|
|
1857
|
+
if (items.length === 0) {
|
|
1858
|
+
return /* @__PURE__ */ jsx31("div", { className: `flex h-full items-center justify-center text-label-13 text-gray-800 ${className}`, children: emptyMessage });
|
|
1859
|
+
}
|
|
1860
|
+
return /* @__PURE__ */ jsx31(
|
|
1861
|
+
"div",
|
|
1862
|
+
{
|
|
1863
|
+
ref: listRef,
|
|
1864
|
+
className: `overflow-y-auto outline-none ${className}`,
|
|
1865
|
+
role: "listbox",
|
|
1866
|
+
tabIndex: 0,
|
|
1867
|
+
onKeyDown: handleKeyDown,
|
|
1868
|
+
"aria-multiselectable": multiSelect,
|
|
1869
|
+
children: items.map((item, i) => {
|
|
1870
|
+
const key = getKey(item);
|
|
1871
|
+
const isSelected = selectedSet.has(key);
|
|
1872
|
+
const isFocused = i === focusIndex;
|
|
1873
|
+
return /* @__PURE__ */ jsx31(
|
|
1874
|
+
"div",
|
|
1875
|
+
{
|
|
1876
|
+
role: "option",
|
|
1877
|
+
"aria-selected": isSelected,
|
|
1878
|
+
className: `cursor-default select-none px-2 py-1 transition-colors
|
|
1879
|
+
${isSelected ? "bg-blue-700/15 text-gray-1000" : "text-gray-1000"}
|
|
1880
|
+
${isFocused && !isSelected ? "bg-gray-alpha-100" : ""}
|
|
1881
|
+
hover:bg-gray-alpha-200`,
|
|
1882
|
+
onClick: () => {
|
|
1883
|
+
setFocusIndex(i);
|
|
1884
|
+
onSelect?.(key);
|
|
1885
|
+
},
|
|
1886
|
+
onDoubleClick: () => onActivate?.(item),
|
|
1887
|
+
children: renderItem(item, { selected: isSelected, focused: isFocused, index: i })
|
|
1888
|
+
},
|
|
1889
|
+
key
|
|
1890
|
+
);
|
|
1891
|
+
})
|
|
1892
|
+
}
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// src/primitives/property-panel.tsx
|
|
1897
|
+
import { jsx as jsx32, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1898
|
+
function PropertyPanelRoot({ title, children, className = "" }) {
|
|
1899
|
+
return /* @__PURE__ */ jsxs24("div", { className: `flex flex-col overflow-y-auto ${className}`, children: [
|
|
1900
|
+
title && /* @__PURE__ */ jsx32("div", { className: "sticky top-0 z-10 border-b border-gray-alpha-200 bg-background-100 px-3 py-2", children: /* @__PURE__ */ jsx32("h3", { className: "text-label-13 font-medium text-gray-1000", children: title }) }),
|
|
1901
|
+
/* @__PURE__ */ jsx32("div", { className: "flex flex-col", children })
|
|
1902
|
+
] });
|
|
1903
|
+
}
|
|
1904
|
+
function PropertySection({
|
|
1905
|
+
title,
|
|
1906
|
+
children,
|
|
1907
|
+
collapsible: _collapsible = false,
|
|
1908
|
+
defaultOpen: _defaultOpen = true
|
|
1909
|
+
}) {
|
|
1910
|
+
return /* @__PURE__ */ jsxs24("div", { className: "border-b border-gray-alpha-200 last:border-b-0", children: [
|
|
1911
|
+
title && /* @__PURE__ */ jsx32("div", { className: "px-3 pt-3 pb-1", children: /* @__PURE__ */ jsx32("span", { className: "text-label-13 font-medium text-gray-800", children: title }) }),
|
|
1912
|
+
/* @__PURE__ */ jsx32("div", { className: `px-3 pb-2 ${title ? "" : "pt-2"}`, children })
|
|
1913
|
+
] });
|
|
1914
|
+
}
|
|
1915
|
+
function PropertyRow({ label, value, children, mono }) {
|
|
1916
|
+
return /* @__PURE__ */ jsxs24("div", { className: "flex items-baseline justify-between gap-4 py-1", children: [
|
|
1917
|
+
/* @__PURE__ */ jsx32("dt", { className: "shrink-0 text-label-13 text-gray-800", children: label }),
|
|
1918
|
+
/* @__PURE__ */ jsx32("dd", { className: `min-w-0 truncate text-right text-label-13 text-gray-1000 ${mono ? "font-mono" : ""}`, children: children ?? value })
|
|
1919
|
+
] });
|
|
1920
|
+
}
|
|
1921
|
+
function PropertySeparator() {
|
|
1922
|
+
return /* @__PURE__ */ jsx32(Separator3, { className: "my-1" });
|
|
1923
|
+
}
|
|
1924
|
+
var PropertyPanel = Object.assign(PropertyPanelRoot, {
|
|
1925
|
+
Section: PropertySection,
|
|
1926
|
+
Row: PropertyRow,
|
|
1927
|
+
Separator: PropertySeparator
|
|
1928
|
+
});
|
|
1929
|
+
|
|
1930
|
+
// src/primitives/section-header.tsx
|
|
1931
|
+
import { jsx as jsx33, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1932
|
+
function SectionHeader({
|
|
1933
|
+
title,
|
|
1934
|
+
description,
|
|
1935
|
+
children
|
|
1936
|
+
}) {
|
|
1937
|
+
return /* @__PURE__ */ jsxs25("div", { className: "mb-4 flex items-start justify-between", children: [
|
|
1938
|
+
/* @__PURE__ */ jsxs25("div", { children: [
|
|
1939
|
+
/* @__PURE__ */ jsx33("h2", { className: "text-copy-13 font-medium text-gray-1000", children: title }),
|
|
1940
|
+
description && /* @__PURE__ */ jsx33("p", { className: "mt-0.5 text-copy-13 text-gray-900", children: description })
|
|
1941
|
+
] }),
|
|
1942
|
+
children
|
|
1943
|
+
] });
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
// src/primitives/sidebar-nav.tsx
|
|
1947
|
+
import { jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
1948
|
+
function SidebarNavRoot({ children, label, title, className = "" }) {
|
|
1949
|
+
return /* @__PURE__ */ jsxs26("nav", { className: `flex flex-col py-2 ${className}`, "aria-label": label, children: [
|
|
1950
|
+
title && /* @__PURE__ */ jsx34("div", { className: "px-3 pb-1", children: /* @__PURE__ */ jsx34("span", { className: "text-copy-13 font-medium text-gray-800", children: title }) }),
|
|
1951
|
+
children
|
|
1952
|
+
] });
|
|
1953
|
+
}
|
|
1954
|
+
function SidebarNavGroup({ children, title, className = "" }) {
|
|
1955
|
+
return /* @__PURE__ */ jsxs26("div", { className, children: [
|
|
1956
|
+
title && /* @__PURE__ */ jsx34("div", { className: "px-3 pt-4 pb-1", children: /* @__PURE__ */ jsx34("span", { className: "text-copy-13 font-medium text-gray-800", children: title }) }),
|
|
1957
|
+
children
|
|
1958
|
+
] });
|
|
1959
|
+
}
|
|
1960
|
+
function SidebarNavItem({ children, active, onClick, icon, suffix, className = "" }) {
|
|
1961
|
+
return /* @__PURE__ */ jsxs26(
|
|
1962
|
+
"button",
|
|
1963
|
+
{
|
|
1964
|
+
onClick,
|
|
1965
|
+
className: `mx-1 flex items-center gap-2 rounded-md px-2 py-1 text-copy-13 transition-colors ${active ? "bg-gray-alpha-200 text-gray-1000" : "text-gray-900 hover:bg-gray-alpha-100 hover:text-gray-1000"} ${className}`,
|
|
1966
|
+
children: [
|
|
1967
|
+
icon && /* @__PURE__ */ jsx34("span", { className: "shrink-0 text-gray-800 [&>svg]:h-3.5 [&>svg]:w-3.5", children: icon }),
|
|
1968
|
+
/* @__PURE__ */ jsx34("span", { className: "min-w-0 truncate", children }),
|
|
1969
|
+
suffix && /* @__PURE__ */ jsx34("span", { className: "ml-auto font-mono text-copy-13 tabular-nums text-gray-700", children: suffix })
|
|
1970
|
+
]
|
|
1971
|
+
}
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1974
|
+
var SidebarNav = Object.assign(SidebarNavRoot, {
|
|
1975
|
+
Group: SidebarNavGroup,
|
|
1976
|
+
Item: SidebarNavItem
|
|
1977
|
+
});
|
|
1978
|
+
|
|
1979
|
+
// src/primitives/split-pane.tsx
|
|
1980
|
+
import { useCallback as useCallback5, useRef as useRef6, useState as useState9, useSyncExternalStore } from "react";
|
|
1981
|
+
import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
1982
|
+
function useMediaQuery(query) {
|
|
1983
|
+
const subscribe = useCallback5(
|
|
1984
|
+
(callback) => {
|
|
1985
|
+
const mql = window.matchMedia(query);
|
|
1986
|
+
mql.addEventListener("change", callback);
|
|
1987
|
+
return () => mql.removeEventListener("change", callback);
|
|
1988
|
+
},
|
|
1989
|
+
[query]
|
|
1990
|
+
);
|
|
1991
|
+
const getSnapshot = useCallback5(() => window.matchMedia(query).matches, [query]);
|
|
1992
|
+
const getServerSnapshot = useCallback5(() => false, []);
|
|
1993
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
1994
|
+
}
|
|
1995
|
+
function SplitPaneRoot({
|
|
1996
|
+
children,
|
|
1997
|
+
direction = "horizontal",
|
|
1998
|
+
defaultSize = 240,
|
|
1999
|
+
min = 100,
|
|
2000
|
+
max = 600,
|
|
2001
|
+
collapseBelow = 640,
|
|
2002
|
+
onResize,
|
|
2003
|
+
className = ""
|
|
2004
|
+
}) {
|
|
2005
|
+
const [size, setSize] = useState9(defaultSize);
|
|
2006
|
+
const containerRef = useRef6(null);
|
|
2007
|
+
const dragging = useRef6(false);
|
|
2008
|
+
const startPos = useRef6(0);
|
|
2009
|
+
const startSize = useRef6(0);
|
|
2010
|
+
const isMobile = useMediaQuery(`(max-width: ${collapseBelow}px)`);
|
|
2011
|
+
const isHorizontal = direction === "horizontal" && !isMobile;
|
|
2012
|
+
const [first, second] = children;
|
|
2013
|
+
const onPointerDown = useCallback5(
|
|
2014
|
+
(e) => {
|
|
2015
|
+
e.preventDefault();
|
|
2016
|
+
dragging.current = true;
|
|
2017
|
+
startPos.current = isHorizontal ? e.clientX : e.clientY;
|
|
2018
|
+
startSize.current = size;
|
|
2019
|
+
e.target.setPointerCapture(e.pointerId);
|
|
2020
|
+
},
|
|
2021
|
+
[isHorizontal, size]
|
|
2022
|
+
);
|
|
2023
|
+
const onPointerMove = useCallback5(
|
|
2024
|
+
(e) => {
|
|
2025
|
+
if (!dragging.current) return;
|
|
2026
|
+
const delta = (isHorizontal ? e.clientX : e.clientY) - startPos.current;
|
|
2027
|
+
const next = Math.min(max, Math.max(min, startSize.current + delta));
|
|
2028
|
+
setSize(next);
|
|
2029
|
+
onResize?.(next);
|
|
2030
|
+
},
|
|
2031
|
+
[isHorizontal, min, max, onResize]
|
|
2032
|
+
);
|
|
2033
|
+
const onPointerUp = useCallback5(() => {
|
|
2034
|
+
dragging.current = false;
|
|
2035
|
+
}, []);
|
|
2036
|
+
if (isMobile) {
|
|
2037
|
+
return /* @__PURE__ */ jsxs27("div", { ref: containerRef, className: `flex flex-col h-full w-full overflow-hidden ${className}`, children: [
|
|
2038
|
+
/* @__PURE__ */ jsx35("div", { className: "shrink-0 overflow-auto border-b border-gray-alpha-200", children: first }),
|
|
2039
|
+
/* @__PURE__ */ jsx35("div", { className: "flex-1 overflow-hidden", children: second })
|
|
2040
|
+
] });
|
|
2041
|
+
}
|
|
2042
|
+
const firstStyle = isHorizontal ? { width: size, minWidth: min, maxWidth: max, flexShrink: 0 } : { height: size, minHeight: min, maxHeight: max, flexShrink: 0 };
|
|
2043
|
+
return /* @__PURE__ */ jsxs27(
|
|
2044
|
+
"div",
|
|
2045
|
+
{
|
|
2046
|
+
ref: containerRef,
|
|
2047
|
+
className: `flex ${isHorizontal ? "flex-row" : "flex-col"} h-full w-full overflow-hidden ${className}`,
|
|
2048
|
+
children: [
|
|
2049
|
+
/* @__PURE__ */ jsx35("div", { style: firstStyle, className: "overflow-hidden", children: first }),
|
|
2050
|
+
/* @__PURE__ */ jsx35(
|
|
2051
|
+
"div",
|
|
2052
|
+
{
|
|
2053
|
+
className: `shrink-0 ${isHorizontal ? "w-px cursor-col-resize hover:w-0.5 hover:bg-blue-700/50 active:w-0.5 active:bg-blue-700" : "h-px cursor-row-resize hover:h-0.5 hover:bg-blue-700/50 active:h-0.5 active:bg-blue-700"} bg-gray-alpha-200 transition-colors`,
|
|
2054
|
+
onPointerDown,
|
|
2055
|
+
onPointerMove,
|
|
2056
|
+
onPointerUp,
|
|
2057
|
+
role: "separator",
|
|
2058
|
+
"aria-orientation": isHorizontal ? "vertical" : "horizontal",
|
|
2059
|
+
tabIndex: 0
|
|
2060
|
+
}
|
|
2061
|
+
),
|
|
2062
|
+
/* @__PURE__ */ jsx35("div", { className: "flex-1 overflow-hidden", children: second })
|
|
2063
|
+
]
|
|
2064
|
+
}
|
|
2065
|
+
);
|
|
2066
|
+
}
|
|
2067
|
+
function Panel({ children, className = "" }) {
|
|
2068
|
+
return /* @__PURE__ */ jsx35("div", { className: `h-full w-full overflow-auto ${className}`, children });
|
|
2069
|
+
}
|
|
2070
|
+
var SplitPane = Object.assign(SplitPaneRoot, {
|
|
2071
|
+
Panel
|
|
2072
|
+
});
|
|
2073
|
+
|
|
2074
|
+
// src/primitives/status-badge.tsx
|
|
2075
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
2076
|
+
function StatusBadge({ status }) {
|
|
2077
|
+
const variant = status === "active" ? "green" : status === "creating" ? "amber" : status === "error" ? "red" : "gray";
|
|
2078
|
+
return /* @__PURE__ */ jsx36(Badge, { variant, size: "sm", contrast: "low", children: status });
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// src/primitives/status-bar.tsx
|
|
2082
|
+
import { jsx as jsx37, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2083
|
+
function StatusBarRoot({ children, className = "" }) {
|
|
2084
|
+
return /* @__PURE__ */ jsx37(
|
|
2085
|
+
"div",
|
|
2086
|
+
{
|
|
2087
|
+
className: `flex h-6 shrink-0 items-center gap-0 border-t border-gray-alpha-200 bg-background-100 px-2 font-mono text-label-13 text-gray-900 ${className}`,
|
|
2088
|
+
role: "status",
|
|
2089
|
+
children
|
|
2090
|
+
}
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
var variantColors = {
|
|
2094
|
+
default: "text-gray-900",
|
|
2095
|
+
success: "text-green-900",
|
|
2096
|
+
warning: "text-amber-900",
|
|
2097
|
+
error: "text-red-900"
|
|
2098
|
+
};
|
|
2099
|
+
function StatusBarItem({ children, icon, variant = "default", onClick, className = "" }) {
|
|
2100
|
+
const classes = `inline-flex items-center gap-1 px-1.5 py-0.5 rounded-sm ${variantColors[variant]} ${onClick ? "cursor-pointer hover:bg-gray-alpha-200 transition-colors" : ""} ${className}`;
|
|
2101
|
+
if (onClick) {
|
|
2102
|
+
return /* @__PURE__ */ jsxs28("button", { className: classes, onClick, children: [
|
|
2103
|
+
icon,
|
|
2104
|
+
children
|
|
2105
|
+
] });
|
|
2106
|
+
}
|
|
2107
|
+
return /* @__PURE__ */ jsxs28("span", { className: classes, children: [
|
|
2108
|
+
icon,
|
|
2109
|
+
children
|
|
2110
|
+
] });
|
|
2111
|
+
}
|
|
2112
|
+
function StatusBarSeparator() {
|
|
2113
|
+
return /* @__PURE__ */ jsx37(Separator3, { orientation: "vertical", className: "mx-0.5 h-3" });
|
|
2114
|
+
}
|
|
2115
|
+
function StatusBarSpacer() {
|
|
2116
|
+
return /* @__PURE__ */ jsx37("div", { className: "flex-1" });
|
|
2117
|
+
}
|
|
2118
|
+
var StatusBar = Object.assign(StatusBarRoot, {
|
|
2119
|
+
Item: StatusBarItem,
|
|
2120
|
+
Separator: StatusBarSeparator,
|
|
2121
|
+
Spacer: StatusBarSpacer
|
|
2122
|
+
});
|
|
2123
|
+
|
|
2124
|
+
// src/primitives/toolbar.tsx
|
|
2125
|
+
import { forwardRef as forwardRef15 } from "react";
|
|
2126
|
+
import { jsx as jsx38 } from "react/jsx-runtime";
|
|
2127
|
+
function ToolbarRoot({ children, className = "" }) {
|
|
2128
|
+
return /* @__PURE__ */ jsx38(
|
|
2129
|
+
"div",
|
|
2130
|
+
{
|
|
2131
|
+
className: `flex h-9 shrink-0 items-center gap-0.5 border-b border-gray-alpha-200 bg-background-100 px-1.5 ${className}`,
|
|
2132
|
+
role: "toolbar",
|
|
2133
|
+
children
|
|
2134
|
+
}
|
|
2135
|
+
);
|
|
2136
|
+
}
|
|
2137
|
+
var ToolbarButton = forwardRef15(function ToolbarButton2({ tooltip, active, children, className = "", disabled, ...props }, ref) {
|
|
2138
|
+
const btn = /* @__PURE__ */ jsx38(
|
|
2139
|
+
"button",
|
|
2140
|
+
{
|
|
2141
|
+
ref,
|
|
2142
|
+
disabled,
|
|
2143
|
+
className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-gray-900 transition-colors [&>svg]:h-3.5 [&>svg]:w-3.5
|
|
2144
|
+
hover:bg-gray-alpha-200 hover:text-gray-1000
|
|
2145
|
+
disabled:pointer-events-none disabled:opacity-40
|
|
2146
|
+
${active ? "bg-gray-alpha-200 text-gray-1000" : ""}
|
|
2147
|
+
${className}`,
|
|
2148
|
+
...props,
|
|
2149
|
+
children
|
|
2150
|
+
}
|
|
2151
|
+
);
|
|
2152
|
+
if (tooltip) {
|
|
2153
|
+
return /* @__PURE__ */ jsx38(Tooltip, { text: tooltip, desktopOnly: true, children: btn });
|
|
2154
|
+
}
|
|
2155
|
+
return btn;
|
|
2156
|
+
});
|
|
2157
|
+
function ToolbarGroup({ children, className = "" }) {
|
|
2158
|
+
return /* @__PURE__ */ jsx38("div", { className: `flex items-center gap-px ${className}`, children });
|
|
2159
|
+
}
|
|
2160
|
+
function ToolbarSeparator() {
|
|
2161
|
+
return /* @__PURE__ */ jsx38(Separator3, { orientation: "vertical", className: "mx-1 h-4" });
|
|
2162
|
+
}
|
|
2163
|
+
function ToolbarSpacer() {
|
|
2164
|
+
return /* @__PURE__ */ jsx38("div", { className: "flex-1" });
|
|
2165
|
+
}
|
|
2166
|
+
function ToolbarText({ children, className = "" }) {
|
|
2167
|
+
return /* @__PURE__ */ jsx38("span", { className: `px-1.5 text-label-13 text-gray-900 ${className}`, children });
|
|
2168
|
+
}
|
|
2169
|
+
function ToolbarInput({
|
|
2170
|
+
value,
|
|
2171
|
+
onChange,
|
|
2172
|
+
placeholder,
|
|
2173
|
+
className = "",
|
|
2174
|
+
readOnly
|
|
2175
|
+
}) {
|
|
2176
|
+
return /* @__PURE__ */ jsx38(
|
|
2177
|
+
"input",
|
|
2178
|
+
{
|
|
2179
|
+
type: "text",
|
|
2180
|
+
value,
|
|
2181
|
+
onChange,
|
|
2182
|
+
placeholder,
|
|
2183
|
+
readOnly,
|
|
2184
|
+
className: `h-6 flex-1 rounded-md border border-gray-alpha-200 bg-gray-alpha-100 px-2 font-mono text-label-13 text-gray-1000 placeholder:text-gray-700 outline-none transition-colors focus:border-blue-700 ${className}`
|
|
2185
|
+
}
|
|
2186
|
+
);
|
|
2187
|
+
}
|
|
2188
|
+
var Toolbar = Object.assign(ToolbarRoot, {
|
|
2189
|
+
Button: ToolbarButton,
|
|
2190
|
+
Group: ToolbarGroup,
|
|
2191
|
+
Separator: ToolbarSeparator,
|
|
2192
|
+
Spacer: ToolbarSpacer,
|
|
2193
|
+
Text: ToolbarText,
|
|
2194
|
+
Input: ToolbarInput
|
|
2195
|
+
});
|
|
2196
|
+
|
|
2197
|
+
// src/stores/notification-store.ts
|
|
2198
|
+
import { create as create2 } from "zustand";
|
|
2199
|
+
var MAX_VISIBLE = 5;
|
|
2200
|
+
var MAX_HISTORY = 50;
|
|
2201
|
+
var PREFS_KEY = "khal_os_notification_prefs";
|
|
2202
|
+
function loadPrefs() {
|
|
2203
|
+
if (typeof window === "undefined") return { doNotDisturb: false, desktopNotifMode: "background" };
|
|
2204
|
+
try {
|
|
2205
|
+
const raw = localStorage.getItem(PREFS_KEY);
|
|
2206
|
+
if (raw) return JSON.parse(raw);
|
|
2207
|
+
} catch {
|
|
2208
|
+
}
|
|
2209
|
+
return { doNotDisturb: false, desktopNotifMode: "background" };
|
|
2210
|
+
}
|
|
2211
|
+
function savePrefs(prefs) {
|
|
2212
|
+
try {
|
|
2213
|
+
localStorage.setItem(PREFS_KEY, JSON.stringify(prefs));
|
|
2214
|
+
} catch {
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
function getBrowserPermission() {
|
|
2218
|
+
if (typeof window === "undefined" || !("Notification" in window)) return "denied";
|
|
2219
|
+
return Notification.permission;
|
|
2220
|
+
}
|
|
2221
|
+
function sendBrowserNotification(notif) {
|
|
2222
|
+
if (typeof window === "undefined" || !("Notification" in window) || Notification.permission !== "granted") return;
|
|
2223
|
+
const n = new Notification(notif.summary, {
|
|
2224
|
+
body: notif.body || void 0,
|
|
2225
|
+
icon: notif.icon || void 0,
|
|
2226
|
+
tag: String(notif.id),
|
|
2227
|
+
silent: notif.urgency === "low"
|
|
2228
|
+
});
|
|
2229
|
+
n.onclick = () => {
|
|
2230
|
+
window.focus();
|
|
2231
|
+
n.close();
|
|
2232
|
+
};
|
|
2233
|
+
if (notif.expires > 0) {
|
|
2234
|
+
setTimeout(() => n.close(), notif.expires);
|
|
2235
|
+
} else if (notif.urgency !== "critical") {
|
|
2236
|
+
setTimeout(() => n.close(), 6e3);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
var initialPrefs = loadPrefs();
|
|
2240
|
+
var useNotificationStore = create2()((set, get) => ({
|
|
2241
|
+
notifications: [],
|
|
2242
|
+
history: [],
|
|
2243
|
+
trayIcons: /* @__PURE__ */ new Map(),
|
|
2244
|
+
centerOpen: false,
|
|
2245
|
+
unreadCount: 0,
|
|
2246
|
+
doNotDisturb: initialPrefs.doNotDisturb,
|
|
2247
|
+
desktopNotifMode: initialPrefs.desktopNotifMode,
|
|
2248
|
+
browserPermission: getBrowserPermission(),
|
|
2249
|
+
setDoNotDisturb: (value) => {
|
|
2250
|
+
set({ doNotDisturb: value });
|
|
2251
|
+
savePrefs({ doNotDisturb: value, desktopNotifMode: get().desktopNotifMode });
|
|
2252
|
+
},
|
|
2253
|
+
setDesktopNotifMode: (mode) => {
|
|
2254
|
+
set({ desktopNotifMode: mode });
|
|
2255
|
+
savePrefs({ doNotDisturb: get().doNotDisturb, desktopNotifMode: mode });
|
|
2256
|
+
},
|
|
2257
|
+
requestBrowserPermission: async () => {
|
|
2258
|
+
if (typeof window === "undefined" || !("Notification" in window)) return "denied";
|
|
2259
|
+
const result = await Notification.requestPermission();
|
|
2260
|
+
set({ browserPermission: result });
|
|
2261
|
+
return result;
|
|
2262
|
+
},
|
|
2263
|
+
syncBrowserPermission: () => {
|
|
2264
|
+
set({ browserPermission: getBrowserPermission() });
|
|
2265
|
+
},
|
|
2266
|
+
addNotification: (notification) => {
|
|
2267
|
+
const full = {
|
|
2268
|
+
...notification,
|
|
2269
|
+
appName: notification.appName ?? "",
|
|
2270
|
+
urgency: notification.urgency ?? "normal",
|
|
2271
|
+
category: notification.category ?? null,
|
|
2272
|
+
transient: notification.transient ?? false,
|
|
2273
|
+
workspaceId: notification.workspaceId ?? null,
|
|
2274
|
+
timestamp: Date.now(),
|
|
2275
|
+
read: false
|
|
2276
|
+
};
|
|
2277
|
+
const { doNotDisturb, desktopNotifMode, browserPermission } = get();
|
|
2278
|
+
set((state) => {
|
|
2279
|
+
const history = full.transient ? state.history : [full, ...state.history].slice(0, MAX_HISTORY);
|
|
2280
|
+
if (doNotDisturb && full.urgency !== "critical") {
|
|
2281
|
+
return {
|
|
2282
|
+
history,
|
|
2283
|
+
unreadCount: full.transient ? state.unreadCount : state.unreadCount + 1
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2286
|
+
let notifications = [...state.notifications];
|
|
2287
|
+
if (notification.replacesId) {
|
|
2288
|
+
notifications = notifications.filter((n) => n.id !== notification.replacesId);
|
|
2289
|
+
}
|
|
2290
|
+
notifications = [full, ...notifications].slice(0, MAX_VISIBLE);
|
|
2291
|
+
return {
|
|
2292
|
+
notifications,
|
|
2293
|
+
history,
|
|
2294
|
+
unreadCount: full.transient ? state.unreadCount : state.unreadCount + 1
|
|
2295
|
+
};
|
|
2296
|
+
});
|
|
2297
|
+
if (browserPermission === "granted" && desktopNotifMode !== "off" && !full.transient) {
|
|
2298
|
+
const shouldSend = desktopNotifMode === "always" || desktopNotifMode === "background" && document.hidden;
|
|
2299
|
+
if (shouldSend) {
|
|
2300
|
+
sendBrowserNotification(full);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
const isCritical = full.urgency === "critical";
|
|
2304
|
+
if (full.expires > 0) {
|
|
2305
|
+
setTimeout(() => {
|
|
2306
|
+
get().hideNotification(full.id);
|
|
2307
|
+
}, full.expires);
|
|
2308
|
+
} else if (!isCritical) {
|
|
2309
|
+
setTimeout(() => {
|
|
2310
|
+
get().hideNotification(full.id);
|
|
2311
|
+
}, 6e3);
|
|
2312
|
+
}
|
|
2313
|
+
},
|
|
2314
|
+
dismissNotification: (id) => {
|
|
2315
|
+
set((state) => ({
|
|
2316
|
+
notifications: state.notifications.filter((n) => n.id !== id)
|
|
2317
|
+
}));
|
|
2318
|
+
},
|
|
2319
|
+
hideNotification: (id) => {
|
|
2320
|
+
set((state) => ({
|
|
2321
|
+
notifications: state.notifications.filter((n) => n.id !== id)
|
|
2322
|
+
}));
|
|
2323
|
+
},
|
|
2324
|
+
clearHistory: () => {
|
|
2325
|
+
set({ history: [], unreadCount: 0 });
|
|
2326
|
+
},
|
|
2327
|
+
markAllRead: () => {
|
|
2328
|
+
set((state) => ({
|
|
2329
|
+
history: state.history.map((n) => ({ ...n, read: true })),
|
|
2330
|
+
unreadCount: 0
|
|
2331
|
+
}));
|
|
2332
|
+
},
|
|
2333
|
+
toggleCenter: () => {
|
|
2334
|
+
const opening = !get().centerOpen;
|
|
2335
|
+
set({ centerOpen: opening });
|
|
2336
|
+
if (opening) {
|
|
2337
|
+
get().markAllRead();
|
|
2338
|
+
}
|
|
2339
|
+
},
|
|
2340
|
+
closeCenter: () => {
|
|
2341
|
+
set({ centerOpen: false });
|
|
2342
|
+
},
|
|
2343
|
+
addTrayIcon: (icon) => {
|
|
2344
|
+
set((state) => {
|
|
2345
|
+
const next = new Map(state.trayIcons);
|
|
2346
|
+
next.set(icon.wid, icon);
|
|
2347
|
+
return { trayIcons: next };
|
|
2348
|
+
});
|
|
2349
|
+
},
|
|
2350
|
+
removeTrayIcon: (wid) => {
|
|
2351
|
+
set((state) => {
|
|
2352
|
+
const next = new Map(state.trayIcons);
|
|
2353
|
+
next.delete(wid);
|
|
2354
|
+
return { trayIcons: next };
|
|
2355
|
+
});
|
|
2356
|
+
},
|
|
2357
|
+
updateTrayIcon: (wid, updates) => {
|
|
2358
|
+
set((state) => {
|
|
2359
|
+
const next = new Map(state.trayIcons);
|
|
2360
|
+
const existing = next.get(wid);
|
|
2361
|
+
if (existing) {
|
|
2362
|
+
next.set(wid, { ...existing, ...updates });
|
|
2363
|
+
}
|
|
2364
|
+
return { trayIcons: next };
|
|
2365
|
+
});
|
|
2366
|
+
}
|
|
2367
|
+
}));
|
|
2368
|
+
|
|
2369
|
+
// src/tokens/lp-tokens.ts
|
|
2370
|
+
var WIN_BG = "#111318";
|
|
2371
|
+
var CHROME_BG = "#0D0F14";
|
|
2372
|
+
var CELL_BG = "#0D1017";
|
|
2373
|
+
var WIN_BORDER = "#1E2330";
|
|
2374
|
+
var WIN_BORDER_FOCUSED = "#333D55";
|
|
2375
|
+
var TEXT_PRIMARY = "#E8EAF0";
|
|
2376
|
+
var TEXT_SECONDARY = "#8B92A5";
|
|
2377
|
+
var TEXT_TERTIARY = "#555D73";
|
|
2378
|
+
var ACCENT_BLUE = "#0A6FE0";
|
|
2379
|
+
var MESH_GRADIENT_PALETTE = [
|
|
2380
|
+
"#030508",
|
|
2381
|
+
"#070D15",
|
|
2382
|
+
"#0C1A2E",
|
|
2383
|
+
"#1A4A7A",
|
|
2384
|
+
"#2A3040",
|
|
2385
|
+
"#5C4A38",
|
|
2386
|
+
"#8B6B42",
|
|
2387
|
+
"#D49355"
|
|
2388
|
+
];
|
|
2389
|
+
var WINDOW_RADIUS = "12px";
|
|
2390
|
+
var BUTTON_RADIUS = "10px";
|
|
2391
|
+
export {
|
|
2392
|
+
ACCENT_BLUE,
|
|
2393
|
+
Avatar,
|
|
2394
|
+
BUTTON_RADIUS,
|
|
2395
|
+
Badge,
|
|
2396
|
+
Button,
|
|
2397
|
+
CELL_BG,
|
|
2398
|
+
CHROME_BG,
|
|
2399
|
+
CollapsibleSidebar,
|
|
2400
|
+
Command,
|
|
2401
|
+
CommandDialog,
|
|
2402
|
+
CommandEmpty,
|
|
2403
|
+
CommandGroup,
|
|
2404
|
+
CommandInput,
|
|
2405
|
+
CommandItem,
|
|
2406
|
+
CommandList,
|
|
2407
|
+
CommandSeparator,
|
|
2408
|
+
CommandShortcut,
|
|
2409
|
+
ContextMenu,
|
|
2410
|
+
ContextMenuContent,
|
|
2411
|
+
ContextMenuGroup,
|
|
2412
|
+
ContextMenuItem,
|
|
2413
|
+
ContextMenuPortal,
|
|
2414
|
+
ContextMenuRadioGroup,
|
|
2415
|
+
ContextMenuSeparator,
|
|
2416
|
+
ContextMenuSub,
|
|
2417
|
+
ContextMenuSubContent,
|
|
2418
|
+
ContextMenuSubTrigger,
|
|
2419
|
+
ContextMenuTrigger,
|
|
2420
|
+
CostCounter,
|
|
2421
|
+
DataRow,
|
|
2422
|
+
Dialog,
|
|
2423
|
+
DropdownMenu,
|
|
2424
|
+
DropdownMenuCheckboxItem,
|
|
2425
|
+
DropdownMenuContent,
|
|
2426
|
+
DropdownMenuGroup,
|
|
2427
|
+
DropdownMenuItem,
|
|
2428
|
+
DropdownMenuLabel,
|
|
2429
|
+
DropdownMenuPortal,
|
|
2430
|
+
DropdownMenuRadioGroup,
|
|
2431
|
+
DropdownMenuRadioItem,
|
|
2432
|
+
DropdownMenuSeparator,
|
|
2433
|
+
DropdownMenuShortcut,
|
|
2434
|
+
DropdownMenuSub,
|
|
2435
|
+
DropdownMenuSubContent,
|
|
2436
|
+
DropdownMenuSubTrigger,
|
|
2437
|
+
DropdownMenuTrigger,
|
|
2438
|
+
EmptyState,
|
|
2439
|
+
GlassCard,
|
|
2440
|
+
Input,
|
|
2441
|
+
KhalLogo,
|
|
2442
|
+
ListView,
|
|
2443
|
+
LiveFeed,
|
|
2444
|
+
MESH_GRADIENT_PALETTE,
|
|
2445
|
+
MeshGradientInner as MeshGradient,
|
|
2446
|
+
MetricDisplay,
|
|
2447
|
+
Note,
|
|
2448
|
+
NumberFlow,
|
|
2449
|
+
PillBadge,
|
|
2450
|
+
ProgressBar,
|
|
2451
|
+
PropertyPanel,
|
|
2452
|
+
SUBJECTS,
|
|
2453
|
+
SectionCard,
|
|
2454
|
+
SectionCardHeader,
|
|
2455
|
+
SectionHeader,
|
|
2456
|
+
Separator3 as Separator,
|
|
2457
|
+
SidebarNav,
|
|
2458
|
+
Spinner,
|
|
2459
|
+
SplitPane,
|
|
2460
|
+
StatusBadge,
|
|
2461
|
+
StatusBar,
|
|
2462
|
+
StatusDot,
|
|
2463
|
+
TEXT_PRIMARY,
|
|
2464
|
+
TEXT_SECONDARY,
|
|
2465
|
+
TEXT_TERTIARY,
|
|
2466
|
+
ThemeProvider,
|
|
2467
|
+
ThemeSwitcher,
|
|
2468
|
+
TickerBar,
|
|
2469
|
+
Toggle,
|
|
2470
|
+
Toolbar,
|
|
2471
|
+
Tooltip,
|
|
2472
|
+
TooltipContent,
|
|
2473
|
+
TooltipProvider,
|
|
2474
|
+
TooltipRoot,
|
|
2475
|
+
TooltipTrigger,
|
|
2476
|
+
WINDOW_RADIUS,
|
|
2477
|
+
WIN_BG,
|
|
2478
|
+
WIN_BORDER,
|
|
2479
|
+
WIN_BORDER_FOCUSED,
|
|
2480
|
+
WindowActiveProvider,
|
|
2481
|
+
WindowMinimizedProvider,
|
|
2482
|
+
badgeVariants,
|
|
2483
|
+
buttonVariants,
|
|
2484
|
+
cn,
|
|
2485
|
+
dataRowVariants,
|
|
2486
|
+
fadeIn,
|
|
2487
|
+
fadeUp,
|
|
2488
|
+
glassCardVariants,
|
|
2489
|
+
khalEasing,
|
|
2490
|
+
metricDisplayVariants,
|
|
2491
|
+
pillBadgeVariants,
|
|
2492
|
+
progressBarVariants,
|
|
2493
|
+
scaleUp,
|
|
2494
|
+
sectionCardVariants,
|
|
2495
|
+
springConfig,
|
|
2496
|
+
staggerChild,
|
|
2497
|
+
staggerContainer,
|
|
2498
|
+
stateConfig,
|
|
2499
|
+
useKhalAuth,
|
|
2500
|
+
useNats,
|
|
2501
|
+
useNatsSubscription,
|
|
2502
|
+
useNotificationStore,
|
|
2503
|
+
useKhalAuth2 as useOSAuth,
|
|
2504
|
+
useReducedMotion,
|
|
2505
|
+
useSidebar,
|
|
2506
|
+
useThemeStore,
|
|
2507
|
+
useWindowActive,
|
|
2508
|
+
useWindowMinimized
|
|
2509
|
+
};
|
|
2510
|
+
//# sourceMappingURL=index.js.map
|