@kinetixui/ui 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +403 -1
- package/dist/index.js +1832 -2
- package/package.json +5 -2
- package/src/components/color-picker.tsx +352 -0
- package/src/components/comparison-slider.tsx +98 -0
- package/src/components/data-grid.tsx +304 -0
- package/src/components/diff-viewer.tsx +155 -0
- package/src/components/json-viewer.tsx +168 -0
- package/src/components/kanban-board.tsx +246 -0
- package/src/components/markdown-editor.tsx +288 -0
- package/src/components/marquee.tsx +57 -0
- package/src/components/message-bubble.tsx +83 -0
- package/src/components/multi-select.tsx +134 -0
- package/src/components/notification-center.tsx +119 -0
- package/src/components/page-header.tsx +43 -0
- package/src/components/tour.tsx +156 -0
- package/src/components/tree-view.tsx +273 -0
- package/src/components/virtual-list.tsx +92 -0
- package/src/index.ts +23 -0
- package/src/stories/ColorPicker.stories.tsx +28 -0
- package/src/stories/ComparisonSlider.stories.tsx +27 -0
- package/src/stories/DataGrid.stories.tsx +54 -0
- package/src/stories/DiffViewer.stories.tsx +26 -0
- package/src/stories/JsonViewer.stories.tsx +31 -0
- package/src/stories/KanbanBoard.stories.tsx +44 -0
- package/src/stories/MarkdownEditor.stories.tsx +25 -0
- package/src/stories/Marquee.stories.tsx +28 -0
- package/src/stories/MessageBubble.stories.tsx +32 -0
- package/src/stories/MultiSelect.stories.tsx +35 -0
- package/src/stories/NotificationCenter.stories.tsx +41 -0
- package/src/stories/PageHeader.stories.tsx +47 -0
- package/src/stories/Tour.stories.tsx +70 -0
- package/src/stories/TreeView.stories.tsx +38 -0
- package/src/stories/VirtualList.stories.tsx +36 -0
- package/tailwind.config.ts +4 -0
package/dist/index.js
CHANGED
|
@@ -3712,14 +3712,14 @@ var NumberInput = React62.forwardRef(
|
|
|
3712
3712
|
({ className, value, defaultValue = 0, min, max, step = 1, onChange, disabled, ...props }, ref) => {
|
|
3713
3713
|
const [internal, setInternal] = React62.useState(defaultValue);
|
|
3714
3714
|
const current = value ?? internal;
|
|
3715
|
-
const
|
|
3715
|
+
const clamp2 = (n) => {
|
|
3716
3716
|
let v = n;
|
|
3717
3717
|
if (min != null) v = Math.max(min, v);
|
|
3718
3718
|
if (max != null) v = Math.min(max, v);
|
|
3719
3719
|
return v;
|
|
3720
3720
|
};
|
|
3721
3721
|
const set = (n) => {
|
|
3722
|
-
const v =
|
|
3722
|
+
const v = clamp2(n);
|
|
3723
3723
|
setInternal(v);
|
|
3724
3724
|
onChange?.(v);
|
|
3725
3725
|
};
|
|
@@ -4206,6 +4206,1816 @@ var Timeline = React73.forwardRef(
|
|
|
4206
4206
|
}) })
|
|
4207
4207
|
);
|
|
4208
4208
|
Timeline.displayName = "Timeline";
|
|
4209
|
+
|
|
4210
|
+
// src/components/marquee.tsx
|
|
4211
|
+
import * as React74 from "react";
|
|
4212
|
+
import { jsx as jsx79, jsxs as jsxs45 } from "react/jsx-runtime";
|
|
4213
|
+
var Marquee = React74.forwardRef(
|
|
4214
|
+
({ className, children, durationSeconds = 32, pauseOnHover = true, style, ...props }, ref) => /* @__PURE__ */ jsx79(
|
|
4215
|
+
"div",
|
|
4216
|
+
{
|
|
4217
|
+
ref,
|
|
4218
|
+
className: cn(
|
|
4219
|
+
"group relative flex overflow-hidden",
|
|
4220
|
+
"[mask-image:linear-gradient(90deg,transparent,black_8%,black_92%,transparent)]",
|
|
4221
|
+
className
|
|
4222
|
+
),
|
|
4223
|
+
style: { ["--marquee-duration"]: `${durationSeconds}s`, ...style },
|
|
4224
|
+
...props,
|
|
4225
|
+
children: /* @__PURE__ */ jsxs45(
|
|
4226
|
+
"div",
|
|
4227
|
+
{
|
|
4228
|
+
className: cn(
|
|
4229
|
+
"flex w-max animate-marquee motion-reduce:animate-none",
|
|
4230
|
+
pauseOnHover && "group-hover:[animation-play-state:paused]"
|
|
4231
|
+
),
|
|
4232
|
+
children: [
|
|
4233
|
+
/* @__PURE__ */ jsx79("div", { className: "flex shrink-0 items-center", children }),
|
|
4234
|
+
/* @__PURE__ */ jsx79("div", { "aria-hidden": "true", className: "flex shrink-0 items-center", children })
|
|
4235
|
+
]
|
|
4236
|
+
}
|
|
4237
|
+
)
|
|
4238
|
+
}
|
|
4239
|
+
)
|
|
4240
|
+
);
|
|
4241
|
+
Marquee.displayName = "Marquee";
|
|
4242
|
+
|
|
4243
|
+
// src/components/comparison-slider.tsx
|
|
4244
|
+
import * as React75 from "react";
|
|
4245
|
+
import * as SliderPrimitive2 from "@radix-ui/react-slider";
|
|
4246
|
+
import { GripVertical as GripVertical2 } from "lucide-react";
|
|
4247
|
+
import { jsx as jsx80, jsxs as jsxs46 } from "react/jsx-runtime";
|
|
4248
|
+
var ComparisonSlider = React75.forwardRef(
|
|
4249
|
+
({ className, before, after, beforeLabel, afterLabel, value, defaultValue = 50, onValueChange, ...props }, ref) => {
|
|
4250
|
+
const [internal, setInternal] = React75.useState(defaultValue);
|
|
4251
|
+
const current = value ?? internal;
|
|
4252
|
+
const handleChange = (vals) => {
|
|
4253
|
+
const v = vals[0] ?? current;
|
|
4254
|
+
setInternal(v);
|
|
4255
|
+
onValueChange?.(v);
|
|
4256
|
+
};
|
|
4257
|
+
return /* @__PURE__ */ jsxs46(
|
|
4258
|
+
"div",
|
|
4259
|
+
{
|
|
4260
|
+
ref,
|
|
4261
|
+
className: cn(
|
|
4262
|
+
"relative aspect-video w-full select-none overflow-hidden rounded-md bg-muted font-sans",
|
|
4263
|
+
"[&_img]:size-full [&_img]:object-cover [&_video]:size-full [&_video]:object-cover",
|
|
4264
|
+
className
|
|
4265
|
+
),
|
|
4266
|
+
...props,
|
|
4267
|
+
children: [
|
|
4268
|
+
/* @__PURE__ */ jsx80("div", { className: "absolute inset-0", children: before }),
|
|
4269
|
+
/* @__PURE__ */ jsx80("div", { className: "absolute inset-0 overflow-hidden", style: { clipPath: `inset(0 0 0 ${current}%)` }, children: after }),
|
|
4270
|
+
/* @__PURE__ */ jsx80(
|
|
4271
|
+
"div",
|
|
4272
|
+
{
|
|
4273
|
+
"aria-hidden": true,
|
|
4274
|
+
className: "absolute inset-y-0 w-0.5 -translate-x-1/2 bg-background shadow-sm",
|
|
4275
|
+
style: { left: `${current}%` }
|
|
4276
|
+
}
|
|
4277
|
+
),
|
|
4278
|
+
beforeLabel && /* @__PURE__ */ jsx80("span", { className: "absolute left-2 top-2 rounded bg-background/80 px-2 py-0.5 text-label-sm text-foreground backdrop-blur-sm", children: beforeLabel }),
|
|
4279
|
+
afterLabel && /* @__PURE__ */ jsx80("span", { className: "absolute right-2 top-2 rounded bg-background/80 px-2 py-0.5 text-label-sm text-foreground backdrop-blur-sm", children: afterLabel }),
|
|
4280
|
+
/* @__PURE__ */ jsxs46(
|
|
4281
|
+
SliderPrimitive2.Root,
|
|
4282
|
+
{
|
|
4283
|
+
value: [current],
|
|
4284
|
+
onValueChange: handleChange,
|
|
4285
|
+
max: 100,
|
|
4286
|
+
step: 0.1,
|
|
4287
|
+
className: "absolute inset-0 flex touch-none items-center",
|
|
4288
|
+
children: [
|
|
4289
|
+
/* @__PURE__ */ jsx80(SliderPrimitive2.Track, { className: "relative h-full w-full grow bg-transparent", children: /* @__PURE__ */ jsx80(SliderPrimitive2.Range, { className: "absolute h-full bg-transparent" }) }),
|
|
4290
|
+
/* @__PURE__ */ jsx80(
|
|
4291
|
+
SliderPrimitive2.Thumb,
|
|
4292
|
+
{
|
|
4293
|
+
"aria-label": "Comparison position",
|
|
4294
|
+
className: "flex size-8 shrink-0 items-center justify-center rounded-full border-2 border-background bg-background/90 text-foreground shadow-md outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
4295
|
+
children: /* @__PURE__ */ jsx80(GripVertical2, { className: "size-4" })
|
|
4296
|
+
}
|
|
4297
|
+
)
|
|
4298
|
+
]
|
|
4299
|
+
}
|
|
4300
|
+
)
|
|
4301
|
+
]
|
|
4302
|
+
}
|
|
4303
|
+
);
|
|
4304
|
+
}
|
|
4305
|
+
);
|
|
4306
|
+
ComparisonSlider.displayName = "ComparisonSlider";
|
|
4307
|
+
|
|
4308
|
+
// src/components/page-header.tsx
|
|
4309
|
+
import * as React76 from "react";
|
|
4310
|
+
import { jsx as jsx81, jsxs as jsxs47 } from "react/jsx-runtime";
|
|
4311
|
+
var PageHeader = React76.forwardRef(
|
|
4312
|
+
({ className, title, description, breadcrumb, actions, tabs, ...props }, ref) => /* @__PURE__ */ jsxs47(
|
|
4313
|
+
"div",
|
|
4314
|
+
{
|
|
4315
|
+
ref,
|
|
4316
|
+
className: cn("flex flex-col gap-4 border-b border-border pb-6 font-sans", className),
|
|
4317
|
+
...props,
|
|
4318
|
+
children: [
|
|
4319
|
+
breadcrumb && /* @__PURE__ */ jsx81("div", { className: "text-body-sm", children: breadcrumb }),
|
|
4320
|
+
/* @__PURE__ */ jsxs47("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [
|
|
4321
|
+
/* @__PURE__ */ jsxs47("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
4322
|
+
/* @__PURE__ */ jsx81("h1", { className: "text-headline-sm font-medium text-foreground", children: title }),
|
|
4323
|
+
description && /* @__PURE__ */ jsx81("p", { className: "text-body-md text-muted-foreground", children: description })
|
|
4324
|
+
] }),
|
|
4325
|
+
actions && /* @__PURE__ */ jsx81("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: actions })
|
|
4326
|
+
] }),
|
|
4327
|
+
tabs && /* @__PURE__ */ jsx81("div", { className: "-mb-6", children: tabs })
|
|
4328
|
+
]
|
|
4329
|
+
}
|
|
4330
|
+
)
|
|
4331
|
+
);
|
|
4332
|
+
PageHeader.displayName = "PageHeader";
|
|
4333
|
+
|
|
4334
|
+
// src/components/notification-center.tsx
|
|
4335
|
+
import * as React77 from "react";
|
|
4336
|
+
import { Bell } from "lucide-react";
|
|
4337
|
+
import { jsx as jsx82, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
4338
|
+
var NotificationCenter = Popover;
|
|
4339
|
+
var NotificationCenterTrigger = React77.forwardRef(
|
|
4340
|
+
({ className, unreadCount = 0, children, ...props }, ref) => /* @__PURE__ */ jsx82(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs48(
|
|
4341
|
+
Button,
|
|
4342
|
+
{
|
|
4343
|
+
ref,
|
|
4344
|
+
variant: "Ghost",
|
|
4345
|
+
size: "icon",
|
|
4346
|
+
"aria-label": unreadCount > 0 ? `Notifications, ${unreadCount} unread` : "Notifications",
|
|
4347
|
+
className: cn("relative", className),
|
|
4348
|
+
...props,
|
|
4349
|
+
children: [
|
|
4350
|
+
children ?? /* @__PURE__ */ jsx82(Bell, { className: "size-5" }),
|
|
4351
|
+
unreadCount > 0 && /* @__PURE__ */ jsx82("span", { className: "absolute right-1.5 top-1.5 flex size-2 rounded-full bg-destructive ring-2 ring-background" })
|
|
4352
|
+
]
|
|
4353
|
+
}
|
|
4354
|
+
) })
|
|
4355
|
+
);
|
|
4356
|
+
NotificationCenterTrigger.displayName = "NotificationCenterTrigger";
|
|
4357
|
+
var NotificationCenterContent = React77.forwardRef(
|
|
4358
|
+
({ className, onMarkAllRead, children, align = "end", ...props }, ref) => /* @__PURE__ */ jsxs48(PopoverContent, { ref, align, className: cn("w-80 p-0", className), ...props, children: [
|
|
4359
|
+
/* @__PURE__ */ jsxs48("div", { className: "flex items-center justify-between border-b border-border px-4 py-3", children: [
|
|
4360
|
+
/* @__PURE__ */ jsx82("p", { className: "text-title-sm font-medium text-foreground", children: "Notifications" }),
|
|
4361
|
+
onMarkAllRead && /* @__PURE__ */ jsx82(
|
|
4362
|
+
"button",
|
|
4363
|
+
{
|
|
4364
|
+
type: "button",
|
|
4365
|
+
onClick: onMarkAllRead,
|
|
4366
|
+
className: "text-label-md font-medium text-primary outline-none hover:underline focus-visible:underline",
|
|
4367
|
+
children: "Mark all read"
|
|
4368
|
+
}
|
|
4369
|
+
)
|
|
4370
|
+
] }),
|
|
4371
|
+
/* @__PURE__ */ jsx82("div", { className: "flex max-h-80 flex-col divide-y divide-border overflow-y-auto", children })
|
|
4372
|
+
] })
|
|
4373
|
+
);
|
|
4374
|
+
NotificationCenterContent.displayName = "NotificationCenterContent";
|
|
4375
|
+
var NotificationItem = React77.forwardRef(
|
|
4376
|
+
({ className, title, description, time, unread, onSelect, ...props }, ref) => {
|
|
4377
|
+
const interactive = !!onSelect;
|
|
4378
|
+
return /* @__PURE__ */ jsxs48(
|
|
4379
|
+
"div",
|
|
4380
|
+
{
|
|
4381
|
+
ref,
|
|
4382
|
+
role: interactive ? "button" : void 0,
|
|
4383
|
+
tabIndex: interactive ? 0 : void 0,
|
|
4384
|
+
onClick: onSelect,
|
|
4385
|
+
onKeyDown: interactive ? (e) => {
|
|
4386
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
4387
|
+
e.preventDefault();
|
|
4388
|
+
onSelect?.();
|
|
4389
|
+
}
|
|
4390
|
+
} : void 0,
|
|
4391
|
+
className: cn(
|
|
4392
|
+
"flex items-start gap-3 px-4 py-3 font-sans outline-none",
|
|
4393
|
+
interactive && "cursor-pointer hover:bg-accent focus-visible:bg-accent",
|
|
4394
|
+
className
|
|
4395
|
+
),
|
|
4396
|
+
...props,
|
|
4397
|
+
children: [
|
|
4398
|
+
/* @__PURE__ */ jsx82("span", { className: cn("mt-1.5 size-2 shrink-0 rounded-full", unread ? "bg-primary" : "bg-transparent") }),
|
|
4399
|
+
/* @__PURE__ */ jsxs48("div", { className: "min-w-0 flex-1", children: [
|
|
4400
|
+
/* @__PURE__ */ jsx82("p", { className: cn("truncate text-body-sm", unread ? "font-medium text-foreground" : "text-muted-foreground"), children: title }),
|
|
4401
|
+
description && /* @__PURE__ */ jsx82("p", { className: "truncate text-body-sm text-muted-foreground", children: description }),
|
|
4402
|
+
time && /* @__PURE__ */ jsx82("p", { className: "mt-0.5 text-label-sm text-muted-foreground", children: time })
|
|
4403
|
+
] })
|
|
4404
|
+
]
|
|
4405
|
+
}
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
);
|
|
4409
|
+
NotificationItem.displayName = "NotificationItem";
|
|
4410
|
+
|
|
4411
|
+
// src/components/tree-view.tsx
|
|
4412
|
+
import * as React78 from "react";
|
|
4413
|
+
import { ChevronRight as ChevronRight7 } from "lucide-react";
|
|
4414
|
+
import { jsx as jsx83, jsxs as jsxs49 } from "react/jsx-runtime";
|
|
4415
|
+
var TreeViewContext = React78.createContext(null);
|
|
4416
|
+
function useTreeViewContext() {
|
|
4417
|
+
const ctx = React78.useContext(TreeViewContext);
|
|
4418
|
+
if (!ctx) throw new Error("TreeItem must be rendered inside a TreeView");
|
|
4419
|
+
return ctx;
|
|
4420
|
+
}
|
|
4421
|
+
var TreeLevelContext = React78.createContext(0);
|
|
4422
|
+
var TreeView = React78.forwardRef(
|
|
4423
|
+
({
|
|
4424
|
+
className,
|
|
4425
|
+
checkable = false,
|
|
4426
|
+
expanded,
|
|
4427
|
+
defaultExpanded = [],
|
|
4428
|
+
onExpandedChange,
|
|
4429
|
+
selected,
|
|
4430
|
+
onSelectedChange,
|
|
4431
|
+
checkedValues,
|
|
4432
|
+
defaultCheckedValues = [],
|
|
4433
|
+
onCheckedChange,
|
|
4434
|
+
children,
|
|
4435
|
+
...props
|
|
4436
|
+
}, ref) => {
|
|
4437
|
+
const [internalExpanded, setInternalExpanded] = React78.useState(defaultExpanded);
|
|
4438
|
+
const currentExpanded = expanded ?? internalExpanded;
|
|
4439
|
+
const [internalChecked, setInternalChecked] = React78.useState(defaultCheckedValues);
|
|
4440
|
+
const currentChecked = checkedValues ?? internalChecked;
|
|
4441
|
+
const [tabbableValue, setTabbableValue] = React78.useState(null);
|
|
4442
|
+
const firstValueRef = React78.useRef(null);
|
|
4443
|
+
const toggleExpanded = React78.useCallback(
|
|
4444
|
+
(value) => {
|
|
4445
|
+
const next = currentExpanded.includes(value) ? currentExpanded.filter((v) => v !== value) : [...currentExpanded, value];
|
|
4446
|
+
setInternalExpanded(next);
|
|
4447
|
+
onExpandedChange?.(next);
|
|
4448
|
+
},
|
|
4449
|
+
[currentExpanded, onExpandedChange]
|
|
4450
|
+
);
|
|
4451
|
+
const toggleChecked = React78.useCallback(
|
|
4452
|
+
(value) => {
|
|
4453
|
+
const next = currentChecked.includes(value) ? currentChecked.filter((v) => v !== value) : [...currentChecked, value];
|
|
4454
|
+
setInternalChecked(next);
|
|
4455
|
+
onCheckedChange?.(next);
|
|
4456
|
+
},
|
|
4457
|
+
[currentChecked, onCheckedChange]
|
|
4458
|
+
);
|
|
4459
|
+
const contextValue = {
|
|
4460
|
+
isExpanded: (value) => currentExpanded.includes(value),
|
|
4461
|
+
toggleExpanded,
|
|
4462
|
+
selected,
|
|
4463
|
+
select: (value) => onSelectedChange?.(value),
|
|
4464
|
+
checkable,
|
|
4465
|
+
isChecked: (value) => currentChecked.includes(value),
|
|
4466
|
+
toggleChecked,
|
|
4467
|
+
tabbableValue,
|
|
4468
|
+
setTabbableValue,
|
|
4469
|
+
firstValueRef
|
|
4470
|
+
};
|
|
4471
|
+
return /* @__PURE__ */ jsx83(
|
|
4472
|
+
"div",
|
|
4473
|
+
{
|
|
4474
|
+
ref,
|
|
4475
|
+
role: "tree",
|
|
4476
|
+
"aria-multiselectable": checkable || void 0,
|
|
4477
|
+
className: cn("flex flex-col font-sans", className),
|
|
4478
|
+
...props,
|
|
4479
|
+
children: /* @__PURE__ */ jsx83(TreeViewContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx83(TreeLevelContext.Provider, { value: 0, children }) })
|
|
4480
|
+
}
|
|
4481
|
+
);
|
|
4482
|
+
}
|
|
4483
|
+
);
|
|
4484
|
+
TreeView.displayName = "TreeView";
|
|
4485
|
+
var TreeItem = React78.forwardRef(
|
|
4486
|
+
({ className, value, label, icon, disabled, children, onClick, onKeyDown, ...props }, ref) => {
|
|
4487
|
+
const ctx = useTreeViewContext();
|
|
4488
|
+
const level = React78.useContext(TreeLevelContext);
|
|
4489
|
+
const hasChildren = React78.Children.count(children) > 0;
|
|
4490
|
+
const expanded = ctx.isExpanded(value);
|
|
4491
|
+
const isChecked = ctx.isChecked(value);
|
|
4492
|
+
if (ctx.firstValueRef.current === null) ctx.firstValueRef.current = value;
|
|
4493
|
+
const isTabbable = (ctx.tabbableValue ?? ctx.firstValueRef.current) === value;
|
|
4494
|
+
const localRef = React78.useRef(null);
|
|
4495
|
+
const setRefs = (node) => {
|
|
4496
|
+
localRef.current = node;
|
|
4497
|
+
if (typeof ref === "function") ref(node);
|
|
4498
|
+
else if (ref) ref.current = node;
|
|
4499
|
+
};
|
|
4500
|
+
const activate = () => {
|
|
4501
|
+
ctx.select(value);
|
|
4502
|
+
if (ctx.checkable) ctx.toggleChecked(value);
|
|
4503
|
+
};
|
|
4504
|
+
const handleKeyDown = (e) => {
|
|
4505
|
+
onKeyDown?.(e);
|
|
4506
|
+
if (e.defaultPrevented || disabled) return;
|
|
4507
|
+
const root = localRef.current?.closest('[role="tree"]');
|
|
4508
|
+
const items = root ? Array.from(root.querySelectorAll('[role="treeitem"]')) : [];
|
|
4509
|
+
const idx = items.indexOf(e.currentTarget);
|
|
4510
|
+
switch (e.key) {
|
|
4511
|
+
case "ArrowDown":
|
|
4512
|
+
e.preventDefault();
|
|
4513
|
+
items[idx + 1]?.focus();
|
|
4514
|
+
break;
|
|
4515
|
+
case "ArrowUp":
|
|
4516
|
+
e.preventDefault();
|
|
4517
|
+
items[idx - 1]?.focus();
|
|
4518
|
+
break;
|
|
4519
|
+
case "Home":
|
|
4520
|
+
e.preventDefault();
|
|
4521
|
+
items[0]?.focus();
|
|
4522
|
+
break;
|
|
4523
|
+
case "End":
|
|
4524
|
+
e.preventDefault();
|
|
4525
|
+
items[items.length - 1]?.focus();
|
|
4526
|
+
break;
|
|
4527
|
+
case "ArrowRight":
|
|
4528
|
+
e.preventDefault();
|
|
4529
|
+
if (hasChildren && !expanded) ctx.toggleExpanded(value);
|
|
4530
|
+
else if (hasChildren) items[idx + 1]?.focus();
|
|
4531
|
+
break;
|
|
4532
|
+
case "ArrowLeft":
|
|
4533
|
+
e.preventDefault();
|
|
4534
|
+
if (hasChildren && expanded) ctx.toggleExpanded(value);
|
|
4535
|
+
else {
|
|
4536
|
+
const parent = localRef.current?.parentElement?.closest('[role="treeitem"]');
|
|
4537
|
+
parent?.focus();
|
|
4538
|
+
}
|
|
4539
|
+
break;
|
|
4540
|
+
case "Enter":
|
|
4541
|
+
case " ":
|
|
4542
|
+
e.preventDefault();
|
|
4543
|
+
activate();
|
|
4544
|
+
break;
|
|
4545
|
+
}
|
|
4546
|
+
};
|
|
4547
|
+
return /* @__PURE__ */ jsxs49(
|
|
4548
|
+
"div",
|
|
4549
|
+
{
|
|
4550
|
+
ref: setRefs,
|
|
4551
|
+
role: "treeitem",
|
|
4552
|
+
"aria-expanded": hasChildren ? expanded : void 0,
|
|
4553
|
+
"aria-selected": ctx.selected === value,
|
|
4554
|
+
"aria-disabled": disabled || void 0,
|
|
4555
|
+
tabIndex: disabled ? void 0 : isTabbable ? 0 : -1,
|
|
4556
|
+
onFocus: () => ctx.setTabbableValue(value),
|
|
4557
|
+
onKeyDown: handleKeyDown,
|
|
4558
|
+
onClick: (e) => {
|
|
4559
|
+
onClick?.(e);
|
|
4560
|
+
if (e.defaultPrevented || disabled) return;
|
|
4561
|
+
activate();
|
|
4562
|
+
},
|
|
4563
|
+
className: cn("outline-none", disabled && "pointer-events-none opacity-disabled", className),
|
|
4564
|
+
...props,
|
|
4565
|
+
children: [
|
|
4566
|
+
/* @__PURE__ */ jsxs49(
|
|
4567
|
+
"div",
|
|
4568
|
+
{
|
|
4569
|
+
style: { paddingLeft: level * 20 + 8 },
|
|
4570
|
+
className: cn(
|
|
4571
|
+
"flex items-center gap-1.5 rounded-sm py-1.5 pr-2 text-body-sm text-foreground",
|
|
4572
|
+
"hover:bg-accent",
|
|
4573
|
+
ctx.selected === value && "bg-accent",
|
|
4574
|
+
"focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring"
|
|
4575
|
+
),
|
|
4576
|
+
children: [
|
|
4577
|
+
hasChildren ? /* @__PURE__ */ jsx83(
|
|
4578
|
+
ChevronRight7,
|
|
4579
|
+
{
|
|
4580
|
+
className: cn("size-4 shrink-0 text-muted-foreground transition-transform", expanded && "rotate-90"),
|
|
4581
|
+
onClick: (e) => {
|
|
4582
|
+
e.stopPropagation();
|
|
4583
|
+
ctx.toggleExpanded(value);
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
) : /* @__PURE__ */ jsx83("span", { className: "size-4 shrink-0" }),
|
|
4587
|
+
ctx.checkable && /* @__PURE__ */ jsx83(
|
|
4588
|
+
Checkbox,
|
|
4589
|
+
{
|
|
4590
|
+
checked: isChecked,
|
|
4591
|
+
onCheckedChange: () => ctx.toggleChecked(value),
|
|
4592
|
+
onClick: (e) => e.stopPropagation(),
|
|
4593
|
+
tabIndex: -1
|
|
4594
|
+
}
|
|
4595
|
+
),
|
|
4596
|
+
icon,
|
|
4597
|
+
/* @__PURE__ */ jsx83("span", { className: "truncate", children: label })
|
|
4598
|
+
]
|
|
4599
|
+
}
|
|
4600
|
+
),
|
|
4601
|
+
hasChildren && expanded && /* @__PURE__ */ jsx83("div", { role: "group", children: /* @__PURE__ */ jsx83(TreeLevelContext.Provider, { value: level + 1, children }) })
|
|
4602
|
+
]
|
|
4603
|
+
}
|
|
4604
|
+
);
|
|
4605
|
+
}
|
|
4606
|
+
);
|
|
4607
|
+
TreeItem.displayName = "TreeItem";
|
|
4608
|
+
|
|
4609
|
+
// src/components/multi-select.tsx
|
|
4610
|
+
import * as React79 from "react";
|
|
4611
|
+
import { Check as Check8 } from "lucide-react";
|
|
4612
|
+
import { jsx as jsx84, jsxs as jsxs50 } from "react/jsx-runtime";
|
|
4613
|
+
var MultiSelect = React79.forwardRef(
|
|
4614
|
+
({ className, options, value, defaultValue = [], onValueChange, placeholder = "Select\u2026", creatable = false, ...props }, ref) => {
|
|
4615
|
+
const [internal, setInternal] = React79.useState(defaultValue);
|
|
4616
|
+
const current = value ?? internal;
|
|
4617
|
+
const [open, setOpen] = React79.useState(false);
|
|
4618
|
+
const [query, setQuery] = React79.useState("");
|
|
4619
|
+
const set = (next) => {
|
|
4620
|
+
setInternal(next);
|
|
4621
|
+
onValueChange?.(next);
|
|
4622
|
+
};
|
|
4623
|
+
const toggle = (v) => set(current.includes(v) ? current.filter((x) => x !== v) : [...current, v]);
|
|
4624
|
+
const remove = (v) => set(current.filter((x) => x !== v));
|
|
4625
|
+
const labelFor = (v) => options.find((o) => o.value === v)?.label ?? v;
|
|
4626
|
+
const trimmed = query.trim();
|
|
4627
|
+
const canCreate = creatable && trimmed.length > 0 && !options.some((o) => o.label.toLowerCase() === trimmed.toLowerCase());
|
|
4628
|
+
const visibleOptions = creatable ? options.filter((o) => o.label.toLowerCase().includes(trimmed.toLowerCase())) : options;
|
|
4629
|
+
return /* @__PURE__ */ jsxs50(Popover, { open, onOpenChange: setOpen, children: [
|
|
4630
|
+
/* @__PURE__ */ jsx84(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs50(
|
|
4631
|
+
"div",
|
|
4632
|
+
{
|
|
4633
|
+
ref,
|
|
4634
|
+
role: "combobox",
|
|
4635
|
+
"aria-expanded": open,
|
|
4636
|
+
"aria-haspopup": "listbox",
|
|
4637
|
+
tabIndex: 0,
|
|
4638
|
+
onClick: () => setOpen(true),
|
|
4639
|
+
onKeyDown: (e) => {
|
|
4640
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
4641
|
+
e.preventDefault();
|
|
4642
|
+
setOpen(true);
|
|
4643
|
+
}
|
|
4644
|
+
},
|
|
4645
|
+
className: cn(
|
|
4646
|
+
"flex min-h-11 w-full flex-wrap items-center gap-1.5 border border-input bg-background px-3 py-2 font-sans",
|
|
4647
|
+
"outline-none focus-visible:border-primary focus-visible:shadow-focus",
|
|
4648
|
+
"aria-expanded:border-primary aria-expanded:shadow-focus",
|
|
4649
|
+
className
|
|
4650
|
+
),
|
|
4651
|
+
...props,
|
|
4652
|
+
children: [
|
|
4653
|
+
current.length === 0 && /* @__PURE__ */ jsx84("span", { className: "text-body-sm text-muted-foreground", children: placeholder }),
|
|
4654
|
+
current.map((v) => /* @__PURE__ */ jsx84(Tag, { variant: "secondary", onRemove: () => remove(v), onClick: (e) => e.stopPropagation(), children: labelFor(v) }, v))
|
|
4655
|
+
]
|
|
4656
|
+
}
|
|
4657
|
+
) }),
|
|
4658
|
+
/* @__PURE__ */ jsx84(
|
|
4659
|
+
PopoverContent,
|
|
4660
|
+
{
|
|
4661
|
+
align: "start",
|
|
4662
|
+
className: "w-[var(--radix-popover-trigger-width)] p-0",
|
|
4663
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
4664
|
+
children: /* @__PURE__ */ jsxs50(Command, { shouldFilter: !creatable, children: [
|
|
4665
|
+
/* @__PURE__ */ jsx84(CommandInput, { placeholder: "Search\u2026", value: query, onValueChange: setQuery }),
|
|
4666
|
+
/* @__PURE__ */ jsxs50(CommandList, { children: [
|
|
4667
|
+
/* @__PURE__ */ jsx84(CommandEmpty, { children: canCreate ? /* @__PURE__ */ jsxs50(
|
|
4668
|
+
"button",
|
|
4669
|
+
{
|
|
4670
|
+
type: "button",
|
|
4671
|
+
className: "flex w-full items-center px-2 py-1.5 text-left text-body-sm text-foreground hover:bg-accent",
|
|
4672
|
+
onClick: () => {
|
|
4673
|
+
toggle(trimmed);
|
|
4674
|
+
setQuery("");
|
|
4675
|
+
},
|
|
4676
|
+
children: [
|
|
4677
|
+
"Create \u201C",
|
|
4678
|
+
trimmed,
|
|
4679
|
+
"\u201D"
|
|
4680
|
+
]
|
|
4681
|
+
}
|
|
4682
|
+
) : "No results." }),
|
|
4683
|
+
/* @__PURE__ */ jsx84(CommandGroup, { children: visibleOptions.map((option) => /* @__PURE__ */ jsxs50(CommandItem, { value: option.label, onSelect: () => toggle(option.value), children: [
|
|
4684
|
+
/* @__PURE__ */ jsx84(Check8, { className: cn("size-4", current.includes(option.value) ? "opacity-100" : "opacity-0") }),
|
|
4685
|
+
option.label
|
|
4686
|
+
] }, option.value)) })
|
|
4687
|
+
] })
|
|
4688
|
+
] })
|
|
4689
|
+
}
|
|
4690
|
+
)
|
|
4691
|
+
] });
|
|
4692
|
+
}
|
|
4693
|
+
);
|
|
4694
|
+
MultiSelect.displayName = "MultiSelect";
|
|
4695
|
+
|
|
4696
|
+
// src/components/message-bubble.tsx
|
|
4697
|
+
import * as React80 from "react";
|
|
4698
|
+
import { Check as Check9, CheckCheck } from "lucide-react";
|
|
4699
|
+
import { jsx as jsx85, jsxs as jsxs51 } from "react/jsx-runtime";
|
|
4700
|
+
var MessageBubble = React80.forwardRef(
|
|
4701
|
+
({ className, variant = "received", timestamp, status, grouped = false, avatar, children, ...props }, ref) => {
|
|
4702
|
+
const sent = variant === "sent";
|
|
4703
|
+
return /* @__PURE__ */ jsxs51("div", { ref, className: cn("flex items-end gap-2 font-sans", sent && "flex-row-reverse", className), ...props, children: [
|
|
4704
|
+
avatar && !sent && /* @__PURE__ */ jsx85("div", { className: "shrink-0 self-end", children: avatar }),
|
|
4705
|
+
/* @__PURE__ */ jsxs51("div", { className: cn("flex max-w-[75%] flex-col gap-1", sent ? "items-end" : "items-start"), children: [
|
|
4706
|
+
/* @__PURE__ */ jsx85(
|
|
4707
|
+
"div",
|
|
4708
|
+
{
|
|
4709
|
+
className: cn(
|
|
4710
|
+
"min-w-0 rounded-2xl px-3.5 py-2 text-body-sm",
|
|
4711
|
+
sent ? "bg-primary text-primary-foreground" : "bg-muted text-foreground",
|
|
4712
|
+
sent && grouped && "rounded-tr-md",
|
|
4713
|
+
!sent && grouped && "rounded-tl-md"
|
|
4714
|
+
),
|
|
4715
|
+
children
|
|
4716
|
+
}
|
|
4717
|
+
),
|
|
4718
|
+
(timestamp || sent && status) && /* @__PURE__ */ jsxs51("div", { className: "flex items-center gap-1 px-1 text-label-sm text-muted-foreground", children: [
|
|
4719
|
+
timestamp,
|
|
4720
|
+
sent && status && /* @__PURE__ */ jsx85("span", { className: cn(status === "read" && "text-primary"), children: status === "sent" ? /* @__PURE__ */ jsx85(Check9, { className: "size-3.5" }) : /* @__PURE__ */ jsx85(CheckCheck, { className: "size-3.5" }) })
|
|
4721
|
+
] })
|
|
4722
|
+
] })
|
|
4723
|
+
] });
|
|
4724
|
+
}
|
|
4725
|
+
);
|
|
4726
|
+
MessageBubble.displayName = "MessageBubble";
|
|
4727
|
+
var TypingIndicator = React80.forwardRef(
|
|
4728
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx85(
|
|
4729
|
+
"div",
|
|
4730
|
+
{
|
|
4731
|
+
ref,
|
|
4732
|
+
role: "status",
|
|
4733
|
+
"aria-label": "Typing",
|
|
4734
|
+
className: cn("flex w-fit items-center gap-1 rounded-2xl bg-muted px-3.5 py-2.5", className),
|
|
4735
|
+
...props,
|
|
4736
|
+
children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx85(
|
|
4737
|
+
"span",
|
|
4738
|
+
{
|
|
4739
|
+
"aria-hidden": true,
|
|
4740
|
+
className: "size-1.5 animate-typing-dot rounded-full bg-muted-foreground",
|
|
4741
|
+
style: { animationDelay: `${i * 150}ms` }
|
|
4742
|
+
},
|
|
4743
|
+
i
|
|
4744
|
+
))
|
|
4745
|
+
}
|
|
4746
|
+
)
|
|
4747
|
+
);
|
|
4748
|
+
TypingIndicator.displayName = "TypingIndicator";
|
|
4749
|
+
|
|
4750
|
+
// src/components/tour.tsx
|
|
4751
|
+
import * as React81 from "react";
|
|
4752
|
+
import { createPortal } from "react-dom";
|
|
4753
|
+
import { Fragment, jsx as jsx86, jsxs as jsxs52 } from "react/jsx-runtime";
|
|
4754
|
+
var SPOTLIGHT_PADDING = 8;
|
|
4755
|
+
var Tour = ({
|
|
4756
|
+
steps,
|
|
4757
|
+
open,
|
|
4758
|
+
stepIndex,
|
|
4759
|
+
onStepIndexChange,
|
|
4760
|
+
onOpenChange,
|
|
4761
|
+
nextLabel = "Next",
|
|
4762
|
+
backLabel = "Back",
|
|
4763
|
+
skipLabel = "Skip",
|
|
4764
|
+
doneLabel = "Done"
|
|
4765
|
+
}) => {
|
|
4766
|
+
const [mounted, setMounted] = React81.useState(false);
|
|
4767
|
+
const [rect, setRect] = React81.useState(null);
|
|
4768
|
+
const step = steps[stepIndex];
|
|
4769
|
+
React81.useEffect(() => setMounted(true), []);
|
|
4770
|
+
React81.useEffect(() => {
|
|
4771
|
+
if (!open || !step) return;
|
|
4772
|
+
const update = () => {
|
|
4773
|
+
const el = document.querySelector(step.target);
|
|
4774
|
+
if (el) {
|
|
4775
|
+
setRect(el.getBoundingClientRect());
|
|
4776
|
+
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
4777
|
+
} else {
|
|
4778
|
+
setRect(null);
|
|
4779
|
+
}
|
|
4780
|
+
};
|
|
4781
|
+
update();
|
|
4782
|
+
window.addEventListener("resize", update);
|
|
4783
|
+
window.addEventListener("scroll", update, true);
|
|
4784
|
+
return () => {
|
|
4785
|
+
window.removeEventListener("resize", update);
|
|
4786
|
+
window.removeEventListener("scroll", update, true);
|
|
4787
|
+
};
|
|
4788
|
+
}, [open, step]);
|
|
4789
|
+
React81.useEffect(() => {
|
|
4790
|
+
if (!open) return;
|
|
4791
|
+
const onKeyDown = (e) => {
|
|
4792
|
+
if (e.key === "Escape") onOpenChange(false);
|
|
4793
|
+
};
|
|
4794
|
+
document.addEventListener("keydown", onKeyDown);
|
|
4795
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
4796
|
+
}, [open, onOpenChange]);
|
|
4797
|
+
if (!mounted || !open || !step) return null;
|
|
4798
|
+
const isFirst = stepIndex === 0;
|
|
4799
|
+
const isLast = stepIndex === steps.length - 1;
|
|
4800
|
+
const spotlightStyle = rect ? {
|
|
4801
|
+
top: rect.top - SPOTLIGHT_PADDING,
|
|
4802
|
+
left: rect.left - SPOTLIGHT_PADDING,
|
|
4803
|
+
width: rect.width + SPOTLIGHT_PADDING * 2,
|
|
4804
|
+
height: rect.height + SPOTLIGHT_PADDING * 2,
|
|
4805
|
+
boxShadow: "0 0 0 9999px rgb(0 0 0 / 0.6)"
|
|
4806
|
+
} : { inset: 0, background: "rgb(0 0 0 / 0.6)" };
|
|
4807
|
+
const cardStyle = rect ? {
|
|
4808
|
+
top: Math.min(rect.bottom + SPOTLIGHT_PADDING + 8, window.innerHeight - 220),
|
|
4809
|
+
left: Math.min(Math.max(rect.left, 16), window.innerWidth - 336)
|
|
4810
|
+
} : { top: "50%", left: "50%", transform: "translate(-50%, -50%)" };
|
|
4811
|
+
return createPortal(
|
|
4812
|
+
/* @__PURE__ */ jsxs52(Fragment, { children: [
|
|
4813
|
+
/* @__PURE__ */ jsx86(
|
|
4814
|
+
"div",
|
|
4815
|
+
{
|
|
4816
|
+
"aria-hidden": true,
|
|
4817
|
+
className: "pointer-events-none fixed z-overlay rounded-lg transition-all duration-fast",
|
|
4818
|
+
style: spotlightStyle
|
|
4819
|
+
}
|
|
4820
|
+
),
|
|
4821
|
+
/* @__PURE__ */ jsxs52(
|
|
4822
|
+
"div",
|
|
4823
|
+
{
|
|
4824
|
+
role: "dialog",
|
|
4825
|
+
"aria-modal": "true",
|
|
4826
|
+
className: "fixed z-overlay w-80 rounded-lg border border-border bg-popover p-4 font-sans text-popover-foreground shadow-lg transition-all duration-fast",
|
|
4827
|
+
style: cardStyle,
|
|
4828
|
+
children: [
|
|
4829
|
+
/* @__PURE__ */ jsx86("p", { className: "text-title-sm font-medium text-foreground", children: step.title }),
|
|
4830
|
+
/* @__PURE__ */ jsx86("div", { className: "mt-1.5 text-body-sm text-muted-foreground", children: step.content }),
|
|
4831
|
+
/* @__PURE__ */ jsxs52("div", { className: "mt-4 flex items-center justify-between", children: [
|
|
4832
|
+
/* @__PURE__ */ jsxs52("span", { className: "text-label-sm text-muted-foreground", children: [
|
|
4833
|
+
stepIndex + 1,
|
|
4834
|
+
" / ",
|
|
4835
|
+
steps.length
|
|
4836
|
+
] }),
|
|
4837
|
+
/* @__PURE__ */ jsxs52("div", { className: "flex items-center gap-2", children: [
|
|
4838
|
+
!isFirst && /* @__PURE__ */ jsx86(Button, { variant: "Ghost", size: "sm", onClick: () => onStepIndexChange(stepIndex - 1), children: backLabel }),
|
|
4839
|
+
!isLast ? /* @__PURE__ */ jsxs52(Fragment, { children: [
|
|
4840
|
+
/* @__PURE__ */ jsx86(Button, { variant: "Link", size: "sm", onClick: () => onOpenChange(false), children: skipLabel }),
|
|
4841
|
+
/* @__PURE__ */ jsx86(Button, { size: "sm", onClick: () => onStepIndexChange(stepIndex + 1), children: nextLabel })
|
|
4842
|
+
] }) : /* @__PURE__ */ jsx86(Button, { size: "sm", onClick: () => onOpenChange(false), children: doneLabel })
|
|
4843
|
+
] })
|
|
4844
|
+
] })
|
|
4845
|
+
]
|
|
4846
|
+
}
|
|
4847
|
+
)
|
|
4848
|
+
] }),
|
|
4849
|
+
document.body
|
|
4850
|
+
);
|
|
4851
|
+
};
|
|
4852
|
+
|
|
4853
|
+
// src/components/virtual-list.tsx
|
|
4854
|
+
import * as React82 from "react";
|
|
4855
|
+
import { jsx as jsx87 } from "react/jsx-runtime";
|
|
4856
|
+
function VirtualList({
|
|
4857
|
+
items,
|
|
4858
|
+
itemHeight,
|
|
4859
|
+
height,
|
|
4860
|
+
renderItem,
|
|
4861
|
+
overscan = 4,
|
|
4862
|
+
getItemKey,
|
|
4863
|
+
className,
|
|
4864
|
+
style,
|
|
4865
|
+
onScroll,
|
|
4866
|
+
...props
|
|
4867
|
+
}) {
|
|
4868
|
+
const containerRef = React82.useRef(null);
|
|
4869
|
+
const [scrollTop, setScrollTop] = React82.useState(0);
|
|
4870
|
+
const [viewportHeight, setViewportHeight] = React82.useState(typeof height === "number" ? height : 0);
|
|
4871
|
+
React82.useEffect(() => {
|
|
4872
|
+
const el = containerRef.current;
|
|
4873
|
+
if (!el) return;
|
|
4874
|
+
const observer = new ResizeObserver((entries) => {
|
|
4875
|
+
const entry = entries[0];
|
|
4876
|
+
if (entry) setViewportHeight(entry.contentRect.height);
|
|
4877
|
+
});
|
|
4878
|
+
observer.observe(el);
|
|
4879
|
+
return () => observer.disconnect();
|
|
4880
|
+
}, []);
|
|
4881
|
+
const totalHeight = items.length * itemHeight;
|
|
4882
|
+
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
|
|
4883
|
+
const visibleCount = Math.ceil(viewportHeight / itemHeight) + overscan * 2;
|
|
4884
|
+
const endIndex = Math.min(items.length, startIndex + Math.max(visibleCount, 0));
|
|
4885
|
+
const visibleItems = items.slice(startIndex, endIndex);
|
|
4886
|
+
return /* @__PURE__ */ jsx87(
|
|
4887
|
+
"div",
|
|
4888
|
+
{
|
|
4889
|
+
ref: containerRef,
|
|
4890
|
+
role: "list",
|
|
4891
|
+
className: cn("overflow-y-auto font-sans", className),
|
|
4892
|
+
style: { height, ...style },
|
|
4893
|
+
onScroll: (e) => {
|
|
4894
|
+
onScroll?.(e);
|
|
4895
|
+
setScrollTop(e.currentTarget.scrollTop);
|
|
4896
|
+
},
|
|
4897
|
+
...props,
|
|
4898
|
+
children: /* @__PURE__ */ jsx87("div", { style: { height: totalHeight, position: "relative" }, children: visibleItems.map((item, i) => {
|
|
4899
|
+
const index = startIndex + i;
|
|
4900
|
+
return /* @__PURE__ */ jsx87(
|
|
4901
|
+
"div",
|
|
4902
|
+
{
|
|
4903
|
+
role: "listitem",
|
|
4904
|
+
style: { position: "absolute", top: index * itemHeight, left: 0, right: 0, height: itemHeight },
|
|
4905
|
+
children: renderItem(item, index)
|
|
4906
|
+
},
|
|
4907
|
+
getItemKey ? getItemKey(item, index) : index
|
|
4908
|
+
);
|
|
4909
|
+
}) })
|
|
4910
|
+
}
|
|
4911
|
+
);
|
|
4912
|
+
}
|
|
4913
|
+
|
|
4914
|
+
// src/components/data-grid.tsx
|
|
4915
|
+
import * as React83 from "react";
|
|
4916
|
+
import { jsx as jsx88, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
4917
|
+
var DEFAULT_WIDTH = 160;
|
|
4918
|
+
var DEFAULT_MIN_WIDTH = 60;
|
|
4919
|
+
var DEFAULT_ROW_HEIGHT = 40;
|
|
4920
|
+
function DataGrid({
|
|
4921
|
+
columns,
|
|
4922
|
+
data,
|
|
4923
|
+
height,
|
|
4924
|
+
rowHeight = DEFAULT_ROW_HEIGHT,
|
|
4925
|
+
overscan = 6,
|
|
4926
|
+
getRowId,
|
|
4927
|
+
className
|
|
4928
|
+
}) {
|
|
4929
|
+
const columnById = React83.useMemo(() => new Map(columns.map((c) => [c.id, c])), [columns]);
|
|
4930
|
+
const [order, setOrder] = React83.useState(() => columns.map((c) => c.id));
|
|
4931
|
+
const [widths, setWidths] = React83.useState(
|
|
4932
|
+
() => Object.fromEntries(columns.map((c) => [c.id, c.width ?? DEFAULT_WIDTH]))
|
|
4933
|
+
);
|
|
4934
|
+
const [sort, setSort] = React83.useState(null);
|
|
4935
|
+
const [editing, setEditing] = React83.useState(null);
|
|
4936
|
+
const dragIdRef = React83.useRef(null);
|
|
4937
|
+
React83.useEffect(() => {
|
|
4938
|
+
const knownIds = columns.map((c) => c.id);
|
|
4939
|
+
const known = new Set(knownIds);
|
|
4940
|
+
setOrder((prev) => {
|
|
4941
|
+
const kept = prev.filter((id) => known.has(id));
|
|
4942
|
+
const added = knownIds.filter((id) => !kept.includes(id));
|
|
4943
|
+
return [...kept, ...added];
|
|
4944
|
+
});
|
|
4945
|
+
setWidths((prev) => {
|
|
4946
|
+
const next = { ...prev };
|
|
4947
|
+
for (const c of columns) if (!(c.id in next)) next[c.id] = c.width ?? DEFAULT_WIDTH;
|
|
4948
|
+
return next;
|
|
4949
|
+
});
|
|
4950
|
+
}, [columns]);
|
|
4951
|
+
const leftPinned = columns.filter((c) => c.pinned === "left");
|
|
4952
|
+
const rightPinned = columns.filter((c) => c.pinned === "right");
|
|
4953
|
+
const unpinnedOrdered = order.filter((id) => columnById.get(id) && !columnById.get(id).pinned).map((id) => columnById.get(id));
|
|
4954
|
+
const renderedColumns = [...leftPinned, ...unpinnedOrdered, ...rightPinned];
|
|
4955
|
+
const leftOffsets = React83.useMemo(() => {
|
|
4956
|
+
const offsets = {};
|
|
4957
|
+
let acc = 0;
|
|
4958
|
+
for (const c of leftPinned) {
|
|
4959
|
+
offsets[c.id] = acc;
|
|
4960
|
+
acc += widths[c.id] ?? c.width ?? DEFAULT_WIDTH;
|
|
4961
|
+
}
|
|
4962
|
+
return offsets;
|
|
4963
|
+
}, [columns, widths]);
|
|
4964
|
+
const rightOffsets = React83.useMemo(() => {
|
|
4965
|
+
const offsets = {};
|
|
4966
|
+
let acc = 0;
|
|
4967
|
+
for (const c of [...rightPinned].reverse()) {
|
|
4968
|
+
offsets[c.id] = acc;
|
|
4969
|
+
acc += widths[c.id] ?? c.width ?? DEFAULT_WIDTH;
|
|
4970
|
+
}
|
|
4971
|
+
return offsets;
|
|
4972
|
+
}, [columns, widths]);
|
|
4973
|
+
const totalWidth = renderedColumns.reduce((sum, c) => sum + (widths[c.id] ?? c.width ?? DEFAULT_WIDTH), 0);
|
|
4974
|
+
const sortedData = React83.useMemo(() => {
|
|
4975
|
+
if (!sort) return data;
|
|
4976
|
+
const col = columnById.get(sort.columnId);
|
|
4977
|
+
if (!col?.value) return data;
|
|
4978
|
+
const indexed = data.map((row, index) => ({ row, index }));
|
|
4979
|
+
indexed.sort((a, b) => {
|
|
4980
|
+
const av = col.value(a.row);
|
|
4981
|
+
const bv = col.value(b.row);
|
|
4982
|
+
if (av === bv) return a.index - b.index;
|
|
4983
|
+
const cmp = av < bv ? -1 : 1;
|
|
4984
|
+
return sort.direction === "asc" ? cmp : -cmp;
|
|
4985
|
+
});
|
|
4986
|
+
return indexed.map((entry) => entry.row);
|
|
4987
|
+
}, [data, sort, columnById]);
|
|
4988
|
+
const containerRef = React83.useRef(null);
|
|
4989
|
+
const [scrollTop, setScrollTop] = React83.useState(0);
|
|
4990
|
+
const [viewportHeight, setViewportHeight] = React83.useState(typeof height === "number" ? height : 0);
|
|
4991
|
+
React83.useEffect(() => {
|
|
4992
|
+
const el = containerRef.current;
|
|
4993
|
+
if (!el) return;
|
|
4994
|
+
const observer = new ResizeObserver((entries) => {
|
|
4995
|
+
const entry = entries[0];
|
|
4996
|
+
if (entry) setViewportHeight(entry.contentRect.height);
|
|
4997
|
+
});
|
|
4998
|
+
observer.observe(el);
|
|
4999
|
+
return () => observer.disconnect();
|
|
5000
|
+
}, []);
|
|
5001
|
+
const totalHeight = sortedData.length * rowHeight;
|
|
5002
|
+
const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
|
|
5003
|
+
const visibleCount = Math.ceil(viewportHeight / rowHeight) + overscan * 2;
|
|
5004
|
+
const endIndex = Math.min(sortedData.length, startIndex + Math.max(visibleCount, 0));
|
|
5005
|
+
const visibleRows = sortedData.slice(startIndex, endIndex);
|
|
5006
|
+
function toggleSort(columnId) {
|
|
5007
|
+
setSort((prev) => {
|
|
5008
|
+
if (!prev || prev.columnId !== columnId) return { columnId, direction: "asc" };
|
|
5009
|
+
if (prev.direction === "asc") return { columnId, direction: "desc" };
|
|
5010
|
+
return null;
|
|
5011
|
+
});
|
|
5012
|
+
}
|
|
5013
|
+
function beginResize(columnId, minWidth, startX, startWidth) {
|
|
5014
|
+
const handleMove = (ev) => {
|
|
5015
|
+
setWidths((prev) => ({ ...prev, [columnId]: Math.max(minWidth, startWidth + (ev.clientX - startX)) }));
|
|
5016
|
+
};
|
|
5017
|
+
const handleUp = () => {
|
|
5018
|
+
window.removeEventListener("pointermove", handleMove);
|
|
5019
|
+
window.removeEventListener("pointerup", handleUp);
|
|
5020
|
+
};
|
|
5021
|
+
window.addEventListener("pointermove", handleMove);
|
|
5022
|
+
window.addEventListener("pointerup", handleUp);
|
|
5023
|
+
}
|
|
5024
|
+
function commitEdit(column, row, rowIndex, value) {
|
|
5025
|
+
column.onCellEdit?.(row, rowIndex, value);
|
|
5026
|
+
setEditing(null);
|
|
5027
|
+
}
|
|
5028
|
+
return /* @__PURE__ */ jsx88(
|
|
5029
|
+
"div",
|
|
5030
|
+
{
|
|
5031
|
+
ref: containerRef,
|
|
5032
|
+
role: "grid",
|
|
5033
|
+
className: cn("relative overflow-auto rounded-md border font-sans text-sm", className),
|
|
5034
|
+
style: { height },
|
|
5035
|
+
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
5036
|
+
children: /* @__PURE__ */ jsxs53("div", { style: { width: totalWidth, minWidth: "100%" }, children: [
|
|
5037
|
+
/* @__PURE__ */ jsx88("div", { role: "row", className: "sticky top-0 z-[2] flex border-b bg-background", children: renderedColumns.map((column) => {
|
|
5038
|
+
const width = widths[column.id] ?? column.width ?? DEFAULT_WIDTH;
|
|
5039
|
+
const pinnedStyle = column.pinned === "left" ? { position: "sticky", left: leftOffsets[column.id], zIndex: 3 } : column.pinned === "right" ? { position: "sticky", right: rightOffsets[column.id], zIndex: 3 } : {};
|
|
5040
|
+
const isSorted = sort?.columnId === column.id;
|
|
5041
|
+
return /* @__PURE__ */ jsxs53(
|
|
5042
|
+
"div",
|
|
5043
|
+
{
|
|
5044
|
+
role: "columnheader",
|
|
5045
|
+
...column.sortable ? { "aria-sort": isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" } : {},
|
|
5046
|
+
draggable: !column.pinned,
|
|
5047
|
+
onDragStart: () => {
|
|
5048
|
+
dragIdRef.current = column.id;
|
|
5049
|
+
},
|
|
5050
|
+
onDragOver: (e) => {
|
|
5051
|
+
if (!column.pinned) e.preventDefault();
|
|
5052
|
+
},
|
|
5053
|
+
onDrop: (e) => {
|
|
5054
|
+
e.preventDefault();
|
|
5055
|
+
const draggedId = dragIdRef.current;
|
|
5056
|
+
dragIdRef.current = null;
|
|
5057
|
+
if (!draggedId || draggedId === column.id) return;
|
|
5058
|
+
setOrder((prev) => {
|
|
5059
|
+
const next = prev.filter((id) => id !== draggedId);
|
|
5060
|
+
const targetIndex = next.indexOf(column.id);
|
|
5061
|
+
next.splice(targetIndex, 0, draggedId);
|
|
5062
|
+
return next;
|
|
5063
|
+
});
|
|
5064
|
+
},
|
|
5065
|
+
onClick: () => column.sortable && toggleSort(column.id),
|
|
5066
|
+
className: cn(
|
|
5067
|
+
"relative flex h-10 shrink-0 select-none items-center gap-1 px-2 font-medium text-muted-foreground",
|
|
5068
|
+
column.sortable && "cursor-pointer hover:text-foreground",
|
|
5069
|
+
column.pinned && "bg-background"
|
|
5070
|
+
),
|
|
5071
|
+
style: { width, ...pinnedStyle },
|
|
5072
|
+
children: [
|
|
5073
|
+
/* @__PURE__ */ jsx88("span", { className: "truncate", children: column.header }),
|
|
5074
|
+
column.sortable && isSorted && /* @__PURE__ */ jsx88("span", { "aria-hidden": "true", className: "text-xs", children: sort.direction === "asc" ? "\u25B2" : "\u25BC" }),
|
|
5075
|
+
/* @__PURE__ */ jsx88(
|
|
5076
|
+
"div",
|
|
5077
|
+
{
|
|
5078
|
+
onPointerDown: (e) => {
|
|
5079
|
+
e.stopPropagation();
|
|
5080
|
+
beginResize(column.id, column.minWidth ?? DEFAULT_MIN_WIDTH, e.clientX, width);
|
|
5081
|
+
},
|
|
5082
|
+
className: "absolute right-0 top-0 h-full w-1 touch-none cursor-col-resize hover:bg-primary/50"
|
|
5083
|
+
}
|
|
5084
|
+
)
|
|
5085
|
+
]
|
|
5086
|
+
},
|
|
5087
|
+
column.id
|
|
5088
|
+
);
|
|
5089
|
+
}) }),
|
|
5090
|
+
/* @__PURE__ */ jsx88("div", { style: { height: totalHeight, position: "relative" }, children: visibleRows.map((row, i) => {
|
|
5091
|
+
const rowIndex = startIndex + i;
|
|
5092
|
+
return /* @__PURE__ */ jsx88(
|
|
5093
|
+
"div",
|
|
5094
|
+
{
|
|
5095
|
+
role: "row",
|
|
5096
|
+
className: "absolute flex w-full border-b hover:bg-muted/50",
|
|
5097
|
+
style: { top: rowIndex * rowHeight, height: rowHeight },
|
|
5098
|
+
children: renderedColumns.map((column) => {
|
|
5099
|
+
const width = widths[column.id] ?? column.width ?? DEFAULT_WIDTH;
|
|
5100
|
+
const pinnedStyle = column.pinned === "left" ? { position: "sticky", left: leftOffsets[column.id], zIndex: 1 } : column.pinned === "right" ? { position: "sticky", right: rightOffsets[column.id], zIndex: 1 } : {};
|
|
5101
|
+
const isEditing = editing?.rowIndex === rowIndex && editing.columnId === column.id;
|
|
5102
|
+
return /* @__PURE__ */ jsx88(
|
|
5103
|
+
"div",
|
|
5104
|
+
{
|
|
5105
|
+
role: "gridcell",
|
|
5106
|
+
onDoubleClick: () => column.editable && setEditing({ rowIndex, columnId: column.id }),
|
|
5107
|
+
className: cn("flex shrink-0 items-center px-2", column.pinned && "bg-background"),
|
|
5108
|
+
style: { width, ...pinnedStyle },
|
|
5109
|
+
children: isEditing ? /* @__PURE__ */ jsx88(
|
|
5110
|
+
"input",
|
|
5111
|
+
{
|
|
5112
|
+
autoFocus: true,
|
|
5113
|
+
defaultValue: column.value ? String(column.value(row)) : "",
|
|
5114
|
+
className: "h-7 w-full rounded-sm border-0 bg-transparent px-1 text-sm outline-none ring-1 ring-inset ring-ring",
|
|
5115
|
+
onKeyDown: (e) => {
|
|
5116
|
+
if (e.key === "Enter") commitEdit(column, row, rowIndex, e.currentTarget.value);
|
|
5117
|
+
else if (e.key === "Escape") setEditing(null);
|
|
5118
|
+
},
|
|
5119
|
+
onBlur: (e) => commitEdit(column, row, rowIndex, e.currentTarget.value)
|
|
5120
|
+
}
|
|
5121
|
+
) : /* @__PURE__ */ jsx88("span", { className: "truncate", children: column.cell(row, rowIndex) })
|
|
5122
|
+
},
|
|
5123
|
+
column.id
|
|
5124
|
+
);
|
|
5125
|
+
})
|
|
5126
|
+
},
|
|
5127
|
+
getRowId ? getRowId(row, rowIndex) : rowIndex
|
|
5128
|
+
);
|
|
5129
|
+
}) })
|
|
5130
|
+
] })
|
|
5131
|
+
}
|
|
5132
|
+
);
|
|
5133
|
+
}
|
|
5134
|
+
|
|
5135
|
+
// src/components/json-viewer.tsx
|
|
5136
|
+
import * as React84 from "react";
|
|
5137
|
+
import { Check as Check10, ChevronRight as ChevronRight8, Copy as Copy2 } from "lucide-react";
|
|
5138
|
+
import { Fragment as Fragment2, jsx as jsx89, jsxs as jsxs54 } from "react/jsx-runtime";
|
|
5139
|
+
var JsonViewer = React84.forwardRef(
|
|
5140
|
+
({ data, expandDepth = 1, hideCopy, className, ...props }, ref) => {
|
|
5141
|
+
const [copied, setCopied] = React84.useState(false);
|
|
5142
|
+
const copy = async () => {
|
|
5143
|
+
try {
|
|
5144
|
+
await navigator.clipboard.writeText(JSON.stringify(data, null, 2));
|
|
5145
|
+
setCopied(true);
|
|
5146
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
5147
|
+
} catch {
|
|
5148
|
+
}
|
|
5149
|
+
};
|
|
5150
|
+
return /* @__PURE__ */ jsxs54(
|
|
5151
|
+
"div",
|
|
5152
|
+
{
|
|
5153
|
+
ref,
|
|
5154
|
+
className: cn("relative overflow-auto rounded-md border bg-muted/30 p-3 font-mono text-xs", className),
|
|
5155
|
+
...props,
|
|
5156
|
+
children: [
|
|
5157
|
+
!hideCopy && /* @__PURE__ */ jsx89(
|
|
5158
|
+
"button",
|
|
5159
|
+
{
|
|
5160
|
+
type: "button",
|
|
5161
|
+
onClick: copy,
|
|
5162
|
+
"aria-label": "Copy JSON",
|
|
5163
|
+
className: "absolute right-2 top-2 inline-flex size-6 items-center justify-center rounded-sm text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
5164
|
+
children: copied ? /* @__PURE__ */ jsx89(Check10, { className: "size-3.5" }) : /* @__PURE__ */ jsx89(Copy2, { className: "size-3.5" })
|
|
5165
|
+
}
|
|
5166
|
+
),
|
|
5167
|
+
/* @__PURE__ */ jsx89("div", { role: "tree", children: /* @__PURE__ */ jsx89(JsonNode, { value: data, depth: 0, expandDepth, isLast: true }) })
|
|
5168
|
+
]
|
|
5169
|
+
}
|
|
5170
|
+
);
|
|
5171
|
+
}
|
|
5172
|
+
);
|
|
5173
|
+
JsonViewer.displayName = "JsonViewer";
|
|
5174
|
+
function JsonNode({
|
|
5175
|
+
name,
|
|
5176
|
+
value,
|
|
5177
|
+
depth,
|
|
5178
|
+
expandDepth,
|
|
5179
|
+
isLast
|
|
5180
|
+
}) {
|
|
5181
|
+
const isContainer = value !== null && typeof value === "object";
|
|
5182
|
+
const [expanded, setExpanded] = React84.useState(depth < expandDepth);
|
|
5183
|
+
if (!isContainer) {
|
|
5184
|
+
return /* @__PURE__ */ jsxs54("div", { role: "treeitem", style: { paddingLeft: depth * 16 }, children: [
|
|
5185
|
+
name !== void 0 && /* @__PURE__ */ jsxs54("span", { className: "text-muted-foreground", children: [
|
|
5186
|
+
JSON.stringify(name),
|
|
5187
|
+
": "
|
|
5188
|
+
] }),
|
|
5189
|
+
/* @__PURE__ */ jsx89(JsonPrimitive, { value }),
|
|
5190
|
+
!isLast && /* @__PURE__ */ jsx89("span", { className: "text-muted-foreground", children: "," })
|
|
5191
|
+
] });
|
|
5192
|
+
}
|
|
5193
|
+
const isArray = Array.isArray(value);
|
|
5194
|
+
const entries = isArray ? value.map((v, i) => [String(i), v]) : Object.entries(value);
|
|
5195
|
+
const openBracket = isArray ? "[" : "{";
|
|
5196
|
+
const closeBracket = isArray ? "]" : "}";
|
|
5197
|
+
if (entries.length === 0) {
|
|
5198
|
+
return /* @__PURE__ */ jsxs54("div", { role: "treeitem", style: { paddingLeft: depth * 16 + 20 }, children: [
|
|
5199
|
+
name !== void 0 && /* @__PURE__ */ jsxs54("span", { className: "text-muted-foreground", children: [
|
|
5200
|
+
JSON.stringify(name),
|
|
5201
|
+
": "
|
|
5202
|
+
] }),
|
|
5203
|
+
/* @__PURE__ */ jsxs54("span", { children: [
|
|
5204
|
+
openBracket,
|
|
5205
|
+
closeBracket
|
|
5206
|
+
] }),
|
|
5207
|
+
!isLast && /* @__PURE__ */ jsx89("span", { className: "text-muted-foreground", children: "," })
|
|
5208
|
+
] });
|
|
5209
|
+
}
|
|
5210
|
+
return /* @__PURE__ */ jsxs54("div", { role: "treeitem", "aria-expanded": expanded, children: [
|
|
5211
|
+
/* @__PURE__ */ jsxs54("div", { style: { paddingLeft: depth * 16 }, className: "flex items-start gap-1", children: [
|
|
5212
|
+
/* @__PURE__ */ jsx89(
|
|
5213
|
+
"button",
|
|
5214
|
+
{
|
|
5215
|
+
type: "button",
|
|
5216
|
+
onClick: () => setExpanded((e) => !e),
|
|
5217
|
+
"aria-label": expanded ? "Collapse" : "Expand",
|
|
5218
|
+
className: "mt-0.5 inline-flex size-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground",
|
|
5219
|
+
children: /* @__PURE__ */ jsx89(ChevronRight8, { className: cn("size-3 transition-transform", expanded && "rotate-90") })
|
|
5220
|
+
}
|
|
5221
|
+
),
|
|
5222
|
+
/* @__PURE__ */ jsxs54("span", { children: [
|
|
5223
|
+
name !== void 0 && /* @__PURE__ */ jsxs54("span", { className: "text-muted-foreground", children: [
|
|
5224
|
+
JSON.stringify(name),
|
|
5225
|
+
": "
|
|
5226
|
+
] }),
|
|
5227
|
+
/* @__PURE__ */ jsx89("span", { children: openBracket }),
|
|
5228
|
+
!expanded && /* @__PURE__ */ jsxs54("span", { className: "text-muted-foreground", children: [
|
|
5229
|
+
" ",
|
|
5230
|
+
entries.length,
|
|
5231
|
+
" ",
|
|
5232
|
+
isArray ? "item" : "key",
|
|
5233
|
+
entries.length === 1 ? "" : "s",
|
|
5234
|
+
" "
|
|
5235
|
+
] }),
|
|
5236
|
+
!expanded && /* @__PURE__ */ jsx89("span", { children: closeBracket }),
|
|
5237
|
+
!expanded && !isLast && /* @__PURE__ */ jsx89("span", { className: "text-muted-foreground", children: "," })
|
|
5238
|
+
] })
|
|
5239
|
+
] }),
|
|
5240
|
+
expanded && /* @__PURE__ */ jsxs54(Fragment2, { children: [
|
|
5241
|
+
entries.map(([key, val], i) => /* @__PURE__ */ jsx89(
|
|
5242
|
+
JsonNode,
|
|
5243
|
+
{
|
|
5244
|
+
name: isArray ? void 0 : key,
|
|
5245
|
+
value: val,
|
|
5246
|
+
depth: depth + 1,
|
|
5247
|
+
expandDepth,
|
|
5248
|
+
isLast: i === entries.length - 1
|
|
5249
|
+
},
|
|
5250
|
+
key
|
|
5251
|
+
)),
|
|
5252
|
+
/* @__PURE__ */ jsxs54("div", { style: { paddingLeft: depth * 16 }, children: [
|
|
5253
|
+
/* @__PURE__ */ jsx89("span", { children: closeBracket }),
|
|
5254
|
+
!isLast && /* @__PURE__ */ jsx89("span", { className: "text-muted-foreground", children: "," })
|
|
5255
|
+
] })
|
|
5256
|
+
] })
|
|
5257
|
+
] });
|
|
5258
|
+
}
|
|
5259
|
+
function JsonPrimitive({ value }) {
|
|
5260
|
+
if (value === null) return /* @__PURE__ */ jsx89("span", { className: "italic text-muted-foreground", children: "null" });
|
|
5261
|
+
if (value === void 0) return /* @__PURE__ */ jsx89("span", { className: "italic text-muted-foreground", children: "undefined" });
|
|
5262
|
+
if (typeof value === "string") return /* @__PURE__ */ jsx89("span", { className: "text-success", children: JSON.stringify(value) });
|
|
5263
|
+
if (typeof value === "number") return /* @__PURE__ */ jsx89("span", { className: "text-info", children: value });
|
|
5264
|
+
if (typeof value === "boolean") return /* @__PURE__ */ jsx89("span", { className: "text-warning", children: String(value) });
|
|
5265
|
+
return /* @__PURE__ */ jsx89("span", { children: String(value) });
|
|
5266
|
+
}
|
|
5267
|
+
|
|
5268
|
+
// src/components/diff-viewer.tsx
|
|
5269
|
+
import * as React85 from "react";
|
|
5270
|
+
import { jsx as jsx90, jsxs as jsxs55 } from "react/jsx-runtime";
|
|
5271
|
+
function computeLineDiff(oldText, newText) {
|
|
5272
|
+
const oldLines = oldText.split("\n");
|
|
5273
|
+
const newLines = newText.split("\n");
|
|
5274
|
+
const n = oldLines.length;
|
|
5275
|
+
const m = newLines.length;
|
|
5276
|
+
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
5277
|
+
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
5278
|
+
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
5279
|
+
dp[i2][j2] = oldLines[i2] === newLines[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
const ops = [];
|
|
5283
|
+
let i = 0;
|
|
5284
|
+
let j = 0;
|
|
5285
|
+
while (i < n && j < m) {
|
|
5286
|
+
if (oldLines[i] === newLines[j]) {
|
|
5287
|
+
ops.push({ type: "equal", oldLine: i + 1, newLine: j + 1, text: oldLines[i] });
|
|
5288
|
+
i++;
|
|
5289
|
+
j++;
|
|
5290
|
+
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
5291
|
+
ops.push({ type: "remove", oldLine: i + 1, text: oldLines[i] });
|
|
5292
|
+
i++;
|
|
5293
|
+
} else {
|
|
5294
|
+
ops.push({ type: "add", newLine: j + 1, text: newLines[j] });
|
|
5295
|
+
j++;
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
while (i < n) {
|
|
5299
|
+
ops.push({ type: "remove", oldLine: i + 1, text: oldLines[i] });
|
|
5300
|
+
i++;
|
|
5301
|
+
}
|
|
5302
|
+
while (j < m) {
|
|
5303
|
+
ops.push({ type: "add", newLine: j + 1, text: newLines[j] });
|
|
5304
|
+
j++;
|
|
5305
|
+
}
|
|
5306
|
+
return ops;
|
|
5307
|
+
}
|
|
5308
|
+
var DiffViewer = React85.forwardRef(
|
|
5309
|
+
({ oldText, newText, mode = "unified", oldLabel = "Before", newLabel = "After", className, ...props }, ref) => {
|
|
5310
|
+
const ops = React85.useMemo(() => computeLineDiff(oldText, newText), [oldText, newText]);
|
|
5311
|
+
return /* @__PURE__ */ jsx90("div", { ref, className: cn("overflow-auto rounded-md border font-mono text-xs", className), ...props, children: mode === "split" ? /* @__PURE__ */ jsx90(SplitDiff, { ops, oldLabel, newLabel }) : /* @__PURE__ */ jsx90(UnifiedDiff, { ops }) });
|
|
5312
|
+
}
|
|
5313
|
+
);
|
|
5314
|
+
DiffViewer.displayName = "DiffViewer";
|
|
5315
|
+
function UnifiedDiff({ ops }) {
|
|
5316
|
+
return /* @__PURE__ */ jsx90("div", { role: "table", children: ops.map((op, i) => /* @__PURE__ */ jsxs55(
|
|
5317
|
+
"div",
|
|
5318
|
+
{
|
|
5319
|
+
role: "row",
|
|
5320
|
+
className: cn("flex", op.type === "add" && "bg-success/10", op.type === "remove" && "bg-destructive/10"),
|
|
5321
|
+
children: [
|
|
5322
|
+
/* @__PURE__ */ jsx90("span", { className: "w-9 shrink-0 select-none border-r px-1.5 text-right text-muted-foreground", children: op.type !== "add" ? op.oldLine : "" }),
|
|
5323
|
+
/* @__PURE__ */ jsx90("span", { className: "w-9 shrink-0 select-none border-r px-1.5 text-right text-muted-foreground", children: op.type !== "remove" ? op.newLine : "" }),
|
|
5324
|
+
/* @__PURE__ */ jsx90(
|
|
5325
|
+
"span",
|
|
5326
|
+
{
|
|
5327
|
+
className: cn(
|
|
5328
|
+
"w-4 shrink-0 select-none text-center",
|
|
5329
|
+
op.type === "add" && "text-success",
|
|
5330
|
+
op.type === "remove" && "text-destructive"
|
|
5331
|
+
),
|
|
5332
|
+
children: op.type === "add" ? "+" : op.type === "remove" ? "\u2212" : ""
|
|
5333
|
+
}
|
|
5334
|
+
),
|
|
5335
|
+
/* @__PURE__ */ jsx90("span", { className: "min-w-0 flex-1 whitespace-pre px-1", children: op.text })
|
|
5336
|
+
]
|
|
5337
|
+
},
|
|
5338
|
+
i
|
|
5339
|
+
)) });
|
|
5340
|
+
}
|
|
5341
|
+
function SplitDiff({ ops, oldLabel, newLabel }) {
|
|
5342
|
+
return /* @__PURE__ */ jsxs55("div", { role: "table", children: [
|
|
5343
|
+
/* @__PURE__ */ jsxs55("div", { className: "flex border-b bg-muted/40 text-muted-foreground", children: [
|
|
5344
|
+
/* @__PURE__ */ jsx90("div", { className: "flex-1 px-2 py-1", children: oldLabel }),
|
|
5345
|
+
/* @__PURE__ */ jsx90("div", { className: "flex-1 border-l px-2 py-1", children: newLabel })
|
|
5346
|
+
] }),
|
|
5347
|
+
ops.map((op, i) => /* @__PURE__ */ jsxs55("div", { role: "row", className: "flex", children: [
|
|
5348
|
+
/* @__PURE__ */ jsxs55("div", { className: cn("flex flex-1 items-start", op.type === "remove" && "bg-destructive/10"), children: [
|
|
5349
|
+
/* @__PURE__ */ jsx90("span", { className: "w-9 shrink-0 select-none px-1.5 text-right text-muted-foreground", children: op.type !== "add" ? op.oldLine : "" }),
|
|
5350
|
+
/* @__PURE__ */ jsx90("span", { className: "min-w-0 flex-1 whitespace-pre px-1", children: op.type !== "add" ? op.text : "" })
|
|
5351
|
+
] }),
|
|
5352
|
+
/* @__PURE__ */ jsxs55("div", { className: cn("flex flex-1 items-start border-l", op.type === "add" && "bg-success/10"), children: [
|
|
5353
|
+
/* @__PURE__ */ jsx90("span", { className: "w-9 shrink-0 select-none px-1.5 text-right text-muted-foreground", children: op.type !== "remove" ? op.newLine : "" }),
|
|
5354
|
+
/* @__PURE__ */ jsx90("span", { className: "min-w-0 flex-1 whitespace-pre px-1", children: op.type !== "remove" ? op.text : "" })
|
|
5355
|
+
] })
|
|
5356
|
+
] }, i))
|
|
5357
|
+
] });
|
|
5358
|
+
}
|
|
5359
|
+
|
|
5360
|
+
// src/components/color-picker.tsx
|
|
5361
|
+
import * as React86 from "react";
|
|
5362
|
+
import * as SliderPrimitive3 from "@radix-ui/react-slider";
|
|
5363
|
+
import { Pipette } from "lucide-react";
|
|
5364
|
+
import { jsx as jsx91, jsxs as jsxs56 } from "react/jsx-runtime";
|
|
5365
|
+
var clamp = (v, min, max) => Math.min(max, Math.max(min, v));
|
|
5366
|
+
function hexToRgba(hex) {
|
|
5367
|
+
const h = hex.replace("#", "");
|
|
5368
|
+
if (h.length === 8) {
|
|
5369
|
+
const n2 = parseInt(h, 16);
|
|
5370
|
+
return [n2 >>> 24 & 255, n2 >>> 16 & 255, n2 >>> 8 & 255, (n2 & 255) / 255];
|
|
5371
|
+
}
|
|
5372
|
+
const n = parseInt(h.length === 3 ? h.replace(/(.)/g, "$1$1") : h, 16);
|
|
5373
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255, 1];
|
|
5374
|
+
}
|
|
5375
|
+
function rgbaToHex(r, g, b, a, includeAlpha) {
|
|
5376
|
+
const toHex = (v) => clamp(Math.round(v), 0, 255).toString(16).padStart(2, "0");
|
|
5377
|
+
const base = `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
5378
|
+
return includeAlpha ? `${base}${toHex(a * 255)}` : base;
|
|
5379
|
+
}
|
|
5380
|
+
function hsvToRgb(h, s, v) {
|
|
5381
|
+
s /= 100;
|
|
5382
|
+
v /= 100;
|
|
5383
|
+
const c = v * s;
|
|
5384
|
+
const hh = h / 60;
|
|
5385
|
+
const x = c * (1 - Math.abs(hh % 2 - 1));
|
|
5386
|
+
let r = 0;
|
|
5387
|
+
let g = 0;
|
|
5388
|
+
let b = 0;
|
|
5389
|
+
if (hh < 1) [r, g, b] = [c, x, 0];
|
|
5390
|
+
else if (hh < 2) [r, g, b] = [x, c, 0];
|
|
5391
|
+
else if (hh < 3) [r, g, b] = [0, c, x];
|
|
5392
|
+
else if (hh < 4) [r, g, b] = [0, x, c];
|
|
5393
|
+
else if (hh < 5) [r, g, b] = [x, 0, c];
|
|
5394
|
+
else [r, g, b] = [c, 0, x];
|
|
5395
|
+
const m = v - c;
|
|
5396
|
+
return [(r + m) * 255, (g + m) * 255, (b + m) * 255];
|
|
5397
|
+
}
|
|
5398
|
+
function rgbToHsv(r, g, b) {
|
|
5399
|
+
r /= 255;
|
|
5400
|
+
g /= 255;
|
|
5401
|
+
b /= 255;
|
|
5402
|
+
const max = Math.max(r, g, b);
|
|
5403
|
+
const min = Math.min(r, g, b);
|
|
5404
|
+
const d = max - min;
|
|
5405
|
+
let h = 0;
|
|
5406
|
+
if (d !== 0) {
|
|
5407
|
+
switch (max) {
|
|
5408
|
+
case r:
|
|
5409
|
+
h = (g - b) / d % 6;
|
|
5410
|
+
break;
|
|
5411
|
+
case g:
|
|
5412
|
+
h = (b - r) / d + 2;
|
|
5413
|
+
break;
|
|
5414
|
+
default:
|
|
5415
|
+
h = (r - g) / d + 4;
|
|
5416
|
+
}
|
|
5417
|
+
h *= 60;
|
|
5418
|
+
if (h < 0) h += 360;
|
|
5419
|
+
}
|
|
5420
|
+
const s = max === 0 ? 0 : d / max;
|
|
5421
|
+
return [h, s * 100, max * 100];
|
|
5422
|
+
}
|
|
5423
|
+
var HEX_PATTERN_ALPHA = /^[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/;
|
|
5424
|
+
var HEX_PATTERN = /^[0-9a-fA-F]{6}$/;
|
|
5425
|
+
var ColorPicker = React86.forwardRef(
|
|
5426
|
+
({ value, onChange, alpha = false, swatches, className, ...props }, ref) => {
|
|
5427
|
+
const [hsv, setHsv] = React86.useState(() => {
|
|
5428
|
+
const [r, g, b] = hexToRgba(value);
|
|
5429
|
+
return rgbToHsv(r, g, b);
|
|
5430
|
+
});
|
|
5431
|
+
const [a, setA] = React86.useState(() => hexToRgba(value)[3]);
|
|
5432
|
+
const lastEmitted = React86.useRef(value);
|
|
5433
|
+
const [h, s, v] = hsv;
|
|
5434
|
+
React86.useEffect(() => {
|
|
5435
|
+
if (value === lastEmitted.current) return;
|
|
5436
|
+
const [r, g, b, na] = hexToRgba(value);
|
|
5437
|
+
setHsv(rgbToHsv(r, g, b));
|
|
5438
|
+
setA(na);
|
|
5439
|
+
lastEmitted.current = value;
|
|
5440
|
+
}, [value]);
|
|
5441
|
+
const commit = React86.useCallback(
|
|
5442
|
+
(nh, ns, nv, na) => {
|
|
5443
|
+
setHsv([nh, ns, nv]);
|
|
5444
|
+
setA(na);
|
|
5445
|
+
const [r, g, b] = hsvToRgb(nh, ns, nv);
|
|
5446
|
+
const hex = rgbaToHex(r, g, b, na, alpha);
|
|
5447
|
+
lastEmitted.current = hex;
|
|
5448
|
+
onChange(hex);
|
|
5449
|
+
},
|
|
5450
|
+
[alpha, onChange]
|
|
5451
|
+
);
|
|
5452
|
+
const squareRef = React86.useRef(null);
|
|
5453
|
+
const draggingRef = React86.useRef(false);
|
|
5454
|
+
const updateFromPoint = React86.useCallback(
|
|
5455
|
+
(clientX, clientY) => {
|
|
5456
|
+
const el = squareRef.current;
|
|
5457
|
+
if (!el) return;
|
|
5458
|
+
const rect = el.getBoundingClientRect();
|
|
5459
|
+
const ns = clamp((clientX - rect.left) / rect.width * 100, 0, 100);
|
|
5460
|
+
const nv = clamp(100 - (clientY - rect.top) / rect.height * 100, 0, 100);
|
|
5461
|
+
commit(h, ns, nv, a);
|
|
5462
|
+
},
|
|
5463
|
+
[commit, h, a]
|
|
5464
|
+
);
|
|
5465
|
+
const handleSquarePointerDown = (e) => {
|
|
5466
|
+
draggingRef.current = true;
|
|
5467
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
5468
|
+
updateFromPoint(e.clientX, e.clientY);
|
|
5469
|
+
};
|
|
5470
|
+
const handleSquarePointerMove = (e) => {
|
|
5471
|
+
if (!draggingRef.current) return;
|
|
5472
|
+
updateFromPoint(e.clientX, e.clientY);
|
|
5473
|
+
};
|
|
5474
|
+
const handleSquarePointerUp = () => {
|
|
5475
|
+
draggingRef.current = false;
|
|
5476
|
+
};
|
|
5477
|
+
const handleSquareKeyDown = (e) => {
|
|
5478
|
+
const step = e.shiftKey ? 10 : 1;
|
|
5479
|
+
if (e.key === "ArrowLeft") commit(h, clamp(s - step, 0, 100), v, a);
|
|
5480
|
+
else if (e.key === "ArrowRight") commit(h, clamp(s + step, 0, 100), v, a);
|
|
5481
|
+
else if (e.key === "ArrowUp") commit(h, s, clamp(v + step, 0, 100), a);
|
|
5482
|
+
else if (e.key === "ArrowDown") commit(h, s, clamp(v - step, 0, 100), a);
|
|
5483
|
+
else return;
|
|
5484
|
+
e.preventDefault();
|
|
5485
|
+
};
|
|
5486
|
+
const [supportsEyeDropper, setSupportsEyeDropper] = React86.useState(false);
|
|
5487
|
+
React86.useEffect(() => setSupportsEyeDropper(typeof window !== "undefined" && !!window.EyeDropper), []);
|
|
5488
|
+
const pickFromScreen = async () => {
|
|
5489
|
+
if (!window.EyeDropper) return;
|
|
5490
|
+
try {
|
|
5491
|
+
const result = await new window.EyeDropper().open();
|
|
5492
|
+
const [r, g, b] = hexToRgba(result.sRGBHex);
|
|
5493
|
+
commit(...rgbToHsv(r, g, b), a);
|
|
5494
|
+
} catch {
|
|
5495
|
+
}
|
|
5496
|
+
};
|
|
5497
|
+
return /* @__PURE__ */ jsxs56("div", { ref, className: cn("flex w-64 flex-col gap-3 font-sans", className), ...props, children: [
|
|
5498
|
+
/* @__PURE__ */ jsx91(
|
|
5499
|
+
"div",
|
|
5500
|
+
{
|
|
5501
|
+
ref: squareRef,
|
|
5502
|
+
role: "slider",
|
|
5503
|
+
tabIndex: 0,
|
|
5504
|
+
"aria-label": "Saturation and value",
|
|
5505
|
+
"aria-valuetext": `saturation ${Math.round(s)}%, value ${Math.round(v)}%`,
|
|
5506
|
+
onPointerDown: handleSquarePointerDown,
|
|
5507
|
+
onPointerMove: handleSquarePointerMove,
|
|
5508
|
+
onPointerUp: handleSquarePointerUp,
|
|
5509
|
+
onKeyDown: handleSquareKeyDown,
|
|
5510
|
+
className: "relative h-40 w-full touch-none rounded-md outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
5511
|
+
style: {
|
|
5512
|
+
backgroundColor: `hsl(${h} 100% 50%)`,
|
|
5513
|
+
backgroundImage: "linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent)"
|
|
5514
|
+
},
|
|
5515
|
+
children: /* @__PURE__ */ jsx91(
|
|
5516
|
+
"div",
|
|
5517
|
+
{
|
|
5518
|
+
className: "absolute size-3.5 -translate-x-1/2 translate-y-1/2 rounded-full border-2 border-white shadow",
|
|
5519
|
+
style: { left: `${s}%`, bottom: `${v}%`, backgroundColor: `hsl(${h} ${s}% ${v}%)` }
|
|
5520
|
+
}
|
|
5521
|
+
)
|
|
5522
|
+
}
|
|
5523
|
+
),
|
|
5524
|
+
/* @__PURE__ */ jsxs56(
|
|
5525
|
+
SliderPrimitive3.Root,
|
|
5526
|
+
{
|
|
5527
|
+
value: [h],
|
|
5528
|
+
min: 0,
|
|
5529
|
+
max: 360,
|
|
5530
|
+
step: 1,
|
|
5531
|
+
onValueChange: ([nh]) => commit(nh ?? h, s, v, a),
|
|
5532
|
+
"aria-label": "Hue",
|
|
5533
|
+
className: "relative flex h-4 w-full touch-none select-none items-center",
|
|
5534
|
+
children: [
|
|
5535
|
+
/* @__PURE__ */ jsx91(
|
|
5536
|
+
SliderPrimitive3.Track,
|
|
5537
|
+
{
|
|
5538
|
+
className: "relative h-3 w-full grow rounded-full",
|
|
5539
|
+
style: {
|
|
5540
|
+
backgroundImage: "linear-gradient(to right, hsl(0 100% 50%), hsl(60 100% 50%), hsl(120 100% 50%), hsl(180 100% 50%), hsl(240 100% 50%), hsl(300 100% 50%), hsl(360 100% 50%))"
|
|
5541
|
+
}
|
|
5542
|
+
}
|
|
5543
|
+
),
|
|
5544
|
+
/* @__PURE__ */ jsx91(SliderPrimitive3.Thumb, { className: "block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" })
|
|
5545
|
+
]
|
|
5546
|
+
}
|
|
5547
|
+
),
|
|
5548
|
+
alpha && /* @__PURE__ */ jsxs56(
|
|
5549
|
+
SliderPrimitive3.Root,
|
|
5550
|
+
{
|
|
5551
|
+
value: [Math.round(a * 100)],
|
|
5552
|
+
min: 0,
|
|
5553
|
+
max: 100,
|
|
5554
|
+
step: 1,
|
|
5555
|
+
onValueChange: ([na]) => commit(h, s, v, (na ?? a * 100) / 100),
|
|
5556
|
+
"aria-label": "Alpha",
|
|
5557
|
+
className: "relative flex h-4 w-full touch-none select-none items-center",
|
|
5558
|
+
children: [
|
|
5559
|
+
/* @__PURE__ */ jsx91(
|
|
5560
|
+
SliderPrimitive3.Track,
|
|
5561
|
+
{
|
|
5562
|
+
className: "relative h-3 w-full grow overflow-hidden rounded-full",
|
|
5563
|
+
style: {
|
|
5564
|
+
backgroundImage: "conic-gradient(#0000001a 90deg, transparent 90deg 180deg, #0000001a 180deg 270deg, transparent 270deg)",
|
|
5565
|
+
backgroundSize: "8px 8px"
|
|
5566
|
+
},
|
|
5567
|
+
children: /* @__PURE__ */ jsx91(
|
|
5568
|
+
"div",
|
|
5569
|
+
{
|
|
5570
|
+
className: "absolute inset-0",
|
|
5571
|
+
style: { backgroundImage: `linear-gradient(to right, transparent, hsl(${h} ${s}% ${v}%))` }
|
|
5572
|
+
}
|
|
5573
|
+
)
|
|
5574
|
+
}
|
|
5575
|
+
),
|
|
5576
|
+
/* @__PURE__ */ jsx91(SliderPrimitive3.Thumb, { className: "block size-4 rounded-full border-2 border-white bg-background shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" })
|
|
5577
|
+
]
|
|
5578
|
+
}
|
|
5579
|
+
),
|
|
5580
|
+
/* @__PURE__ */ jsxs56("div", { className: "flex items-center gap-2", children: [
|
|
5581
|
+
/* @__PURE__ */ jsx91(
|
|
5582
|
+
"div",
|
|
5583
|
+
{
|
|
5584
|
+
className: "size-8 shrink-0 rounded-md border",
|
|
5585
|
+
style: {
|
|
5586
|
+
backgroundImage: "conic-gradient(#0000001a 90deg, transparent 90deg 180deg, #0000001a 180deg 270deg, transparent 270deg), linear-gradient(hsl(0 0% 0% / 0), hsl(0 0% 0% / 0))",
|
|
5587
|
+
backgroundSize: "8px 8px"
|
|
5588
|
+
},
|
|
5589
|
+
children: /* @__PURE__ */ jsx91("div", { className: "size-full rounded-md", style: { backgroundColor: value } })
|
|
5590
|
+
}
|
|
5591
|
+
),
|
|
5592
|
+
/* @__PURE__ */ jsxs56("div", { className: "flex flex-1 items-center gap-1", children: [
|
|
5593
|
+
/* @__PURE__ */ jsx91("span", { className: "text-muted-foreground", children: "#" }),
|
|
5594
|
+
/* @__PURE__ */ jsx91(HexField, { value, alpha, onCommit: onChange })
|
|
5595
|
+
] }),
|
|
5596
|
+
supportsEyeDropper && /* @__PURE__ */ jsx91(
|
|
5597
|
+
Button,
|
|
5598
|
+
{
|
|
5599
|
+
type: "button",
|
|
5600
|
+
variant: "Ghost",
|
|
5601
|
+
size: "icon",
|
|
5602
|
+
"aria-label": "Pick color from screen",
|
|
5603
|
+
onClick: pickFromScreen,
|
|
5604
|
+
children: /* @__PURE__ */ jsx91(Pipette, { className: "size-4" })
|
|
5605
|
+
}
|
|
5606
|
+
)
|
|
5607
|
+
] }),
|
|
5608
|
+
swatches && swatches.length > 0 && /* @__PURE__ */ jsx91("div", { className: "flex flex-wrap gap-1.5", children: swatches.map((sw) => /* @__PURE__ */ jsx91(
|
|
5609
|
+
"button",
|
|
5610
|
+
{
|
|
5611
|
+
type: "button",
|
|
5612
|
+
"aria-label": sw,
|
|
5613
|
+
onClick: () => {
|
|
5614
|
+
const [r, g, b] = hexToRgba(sw);
|
|
5615
|
+
commit(...rgbToHsv(r, g, b), a);
|
|
5616
|
+
},
|
|
5617
|
+
className: "size-6 rounded-sm border transition-shadow hover:ring-1 hover:ring-ring hover:ring-offset-1",
|
|
5618
|
+
style: { backgroundColor: sw }
|
|
5619
|
+
},
|
|
5620
|
+
sw
|
|
5621
|
+
)) })
|
|
5622
|
+
] });
|
|
5623
|
+
}
|
|
5624
|
+
);
|
|
5625
|
+
ColorPicker.displayName = "ColorPicker";
|
|
5626
|
+
function HexField({
|
|
5627
|
+
value,
|
|
5628
|
+
alpha,
|
|
5629
|
+
onCommit
|
|
5630
|
+
}) {
|
|
5631
|
+
const [draft, setDraft] = React86.useState(value.replace("#", "").toUpperCase());
|
|
5632
|
+
React86.useEffect(() => setDraft(value.replace("#", "").toUpperCase()), [value]);
|
|
5633
|
+
const commit = () => {
|
|
5634
|
+
const pattern = alpha ? HEX_PATTERN_ALPHA : HEX_PATTERN;
|
|
5635
|
+
if (pattern.test(draft)) {
|
|
5636
|
+
onCommit(`#${draft.toLowerCase()}`);
|
|
5637
|
+
} else {
|
|
5638
|
+
setDraft(value.replace("#", "").toUpperCase());
|
|
5639
|
+
}
|
|
5640
|
+
};
|
|
5641
|
+
return /* @__PURE__ */ jsx91(
|
|
5642
|
+
Input,
|
|
5643
|
+
{
|
|
5644
|
+
value: draft,
|
|
5645
|
+
onChange: (e) => setDraft(e.target.value.replace(/[^0-9a-fA-F]/g, "").slice(0, alpha ? 8 : 6)),
|
|
5646
|
+
onBlur: commit,
|
|
5647
|
+
onKeyDown: (e) => {
|
|
5648
|
+
if (e.key === "Enter") {
|
|
5649
|
+
commit();
|
|
5650
|
+
e.currentTarget.blur();
|
|
5651
|
+
}
|
|
5652
|
+
},
|
|
5653
|
+
className: "h-8 flex-1 px-2 py-1 font-mono text-xs uppercase"
|
|
5654
|
+
}
|
|
5655
|
+
);
|
|
5656
|
+
}
|
|
5657
|
+
|
|
5658
|
+
// src/components/kanban-board.tsx
|
|
5659
|
+
import * as React87 from "react";
|
|
5660
|
+
import {
|
|
5661
|
+
DndContext,
|
|
5662
|
+
DragOverlay,
|
|
5663
|
+
KeyboardSensor,
|
|
5664
|
+
PointerSensor,
|
|
5665
|
+
closestCenter,
|
|
5666
|
+
getFirstCollision,
|
|
5667
|
+
pointerWithin,
|
|
5668
|
+
rectIntersection,
|
|
5669
|
+
useDroppable,
|
|
5670
|
+
useSensor,
|
|
5671
|
+
useSensors
|
|
5672
|
+
} from "@dnd-kit/core";
|
|
5673
|
+
import {
|
|
5674
|
+
SortableContext,
|
|
5675
|
+
arrayMove,
|
|
5676
|
+
sortableKeyboardCoordinates,
|
|
5677
|
+
useSortable,
|
|
5678
|
+
verticalListSortingStrategy
|
|
5679
|
+
} from "@dnd-kit/sortable";
|
|
5680
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
5681
|
+
import { jsx as jsx92, jsxs as jsxs57 } from "react/jsx-runtime";
|
|
5682
|
+
var KanbanBoard = React87.forwardRef(
|
|
5683
|
+
({ columns, onColumnsChange, className, ...props }, ref) => {
|
|
5684
|
+
const sensors = useSensors(
|
|
5685
|
+
useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
5686
|
+
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
|
5687
|
+
);
|
|
5688
|
+
const [activeCard, setActiveCard] = React87.useState(null);
|
|
5689
|
+
const lastOverId = React87.useRef(null);
|
|
5690
|
+
const findColumn = React87.useCallback(
|
|
5691
|
+
(id) => columns.find((col) => col.id === id || col.cards.some((c) => c.id === id)),
|
|
5692
|
+
[columns]
|
|
5693
|
+
);
|
|
5694
|
+
const collisionDetectionStrategy = React87.useCallback(
|
|
5695
|
+
(args) => {
|
|
5696
|
+
const pointerIntersections = pointerWithin(args);
|
|
5697
|
+
const intersections = pointerIntersections.length > 0 ? pointerIntersections : rectIntersection(args);
|
|
5698
|
+
let overId = getFirstCollision(intersections, "id");
|
|
5699
|
+
if (overId != null) {
|
|
5700
|
+
const overColumn = columns.find((col) => col.id === overId);
|
|
5701
|
+
if (overColumn && overColumn.cards.length > 0) {
|
|
5702
|
+
const cardIds = new Set(overColumn.cards.map((c) => c.id));
|
|
5703
|
+
const cardCollision = getFirstCollision(
|
|
5704
|
+
closestCenter({
|
|
5705
|
+
...args,
|
|
5706
|
+
droppableContainers: args.droppableContainers.filter((c) => cardIds.has(String(c.id)))
|
|
5707
|
+
}),
|
|
5708
|
+
"id"
|
|
5709
|
+
);
|
|
5710
|
+
if (cardCollision != null) overId = cardCollision;
|
|
5711
|
+
}
|
|
5712
|
+
lastOverId.current = String(overId);
|
|
5713
|
+
return [{ id: overId }];
|
|
5714
|
+
}
|
|
5715
|
+
return lastOverId.current ? [{ id: lastOverId.current }] : [];
|
|
5716
|
+
},
|
|
5717
|
+
[columns]
|
|
5718
|
+
);
|
|
5719
|
+
function handleDragStart(event) {
|
|
5720
|
+
lastOverId.current = String(event.active.id);
|
|
5721
|
+
const column = findColumn(String(event.active.id));
|
|
5722
|
+
const card = column?.cards.find((c) => c.id === event.active.id);
|
|
5723
|
+
setActiveCard(card ?? null);
|
|
5724
|
+
}
|
|
5725
|
+
function handleDragOver(event) {
|
|
5726
|
+
const { active, over } = event;
|
|
5727
|
+
if (!over) return;
|
|
5728
|
+
const activeId = String(active.id);
|
|
5729
|
+
const overId = String(over.id);
|
|
5730
|
+
if (activeId === overId) return;
|
|
5731
|
+
const activeColumn = findColumn(activeId);
|
|
5732
|
+
const overColumn = findColumn(overId);
|
|
5733
|
+
if (!activeColumn || !overColumn || activeColumn.id === overColumn.id) return;
|
|
5734
|
+
onColumnsChange(
|
|
5735
|
+
columns.map((col) => {
|
|
5736
|
+
if (col.id === activeColumn.id) {
|
|
5737
|
+
return { ...col, cards: col.cards.filter((c) => c.id !== activeId) };
|
|
5738
|
+
}
|
|
5739
|
+
if (col.id === overColumn.id) {
|
|
5740
|
+
const movedCard = activeColumn.cards.find((c) => c.id === activeId);
|
|
5741
|
+
if (!movedCard) return col;
|
|
5742
|
+
const overIndex = col.cards.findIndex((c) => c.id === overId);
|
|
5743
|
+
const insertAt = overIndex >= 0 ? overIndex : col.cards.length;
|
|
5744
|
+
const nextCards = [...col.cards];
|
|
5745
|
+
nextCards.splice(insertAt, 0, movedCard);
|
|
5746
|
+
return { ...col, cards: nextCards };
|
|
5747
|
+
}
|
|
5748
|
+
return col;
|
|
5749
|
+
})
|
|
5750
|
+
);
|
|
5751
|
+
}
|
|
5752
|
+
function handleDragEnd(event) {
|
|
5753
|
+
setActiveCard(null);
|
|
5754
|
+
lastOverId.current = null;
|
|
5755
|
+
const { active, over } = event;
|
|
5756
|
+
if (!over) return;
|
|
5757
|
+
const activeId = String(active.id);
|
|
5758
|
+
const overId = String(over.id);
|
|
5759
|
+
const activeColumn = findColumn(activeId);
|
|
5760
|
+
const overColumn = columns.find((col) => col.id === overId) ?? findColumn(overId);
|
|
5761
|
+
if (!activeColumn || !overColumn || activeColumn.id !== overColumn.id) return;
|
|
5762
|
+
const activeIndex = activeColumn.cards.findIndex((c) => c.id === activeId);
|
|
5763
|
+
const rawOverIndex = overColumn.cards.findIndex((c) => c.id === overId);
|
|
5764
|
+
const overIndex = rawOverIndex >= 0 ? rawOverIndex : overColumn.cards.length - 1;
|
|
5765
|
+
if (activeIndex === -1 || overIndex === -1 || activeIndex === overIndex) return;
|
|
5766
|
+
onColumnsChange(
|
|
5767
|
+
columns.map(
|
|
5768
|
+
(col) => col.id === activeColumn.id ? { ...col, cards: arrayMove(col.cards, activeIndex, overIndex) } : col
|
|
5769
|
+
)
|
|
5770
|
+
);
|
|
5771
|
+
}
|
|
5772
|
+
return /* @__PURE__ */ jsxs57(
|
|
5773
|
+
DndContext,
|
|
5774
|
+
{
|
|
5775
|
+
sensors,
|
|
5776
|
+
collisionDetection: collisionDetectionStrategy,
|
|
5777
|
+
onDragStart: handleDragStart,
|
|
5778
|
+
onDragOver: handleDragOver,
|
|
5779
|
+
onDragEnd: handleDragEnd,
|
|
5780
|
+
onDragCancel: () => {
|
|
5781
|
+
setActiveCard(null);
|
|
5782
|
+
lastOverId.current = null;
|
|
5783
|
+
},
|
|
5784
|
+
children: [
|
|
5785
|
+
/* @__PURE__ */ jsx92("div", { ref, role: "group", className: cn("flex items-start gap-4 overflow-x-auto font-sans", className), ...props, children: columns.map((column) => /* @__PURE__ */ jsx92(KanbanColumnView, { column }, column.id)) }),
|
|
5786
|
+
/* @__PURE__ */ jsx92(DragOverlay, { children: activeCard ? /* @__PURE__ */ jsx92(KanbanCardView, { card: activeCard }) : null })
|
|
5787
|
+
]
|
|
5788
|
+
}
|
|
5789
|
+
);
|
|
5790
|
+
}
|
|
5791
|
+
);
|
|
5792
|
+
KanbanBoard.displayName = "KanbanBoard";
|
|
5793
|
+
function KanbanColumnView({ column }) {
|
|
5794
|
+
const { setNodeRef } = useDroppable({ id: column.id });
|
|
5795
|
+
return /* @__PURE__ */ jsxs57("div", { className: "flex w-64 shrink-0 flex-col gap-2 rounded-md border bg-muted/30 p-2", children: [
|
|
5796
|
+
/* @__PURE__ */ jsxs57("div", { className: "flex items-center justify-between px-1 py-1", children: [
|
|
5797
|
+
/* @__PURE__ */ jsx92("span", { className: "text-sm font-medium text-foreground", children: column.title }),
|
|
5798
|
+
/* @__PURE__ */ jsx92("span", { className: "text-xs text-muted-foreground", children: column.cards.length })
|
|
5799
|
+
] }),
|
|
5800
|
+
/* @__PURE__ */ jsx92(SortableContext, { items: column.cards.map((c) => c.id), strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsx92("div", { ref: setNodeRef, className: "flex min-h-10 flex-col gap-2", children: column.cards.map((card) => /* @__PURE__ */ jsx92(SortableKanbanCard, { card }, card.id)) }) })
|
|
5801
|
+
] });
|
|
5802
|
+
}
|
|
5803
|
+
function SortableKanbanCard({ card }) {
|
|
5804
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: card.id });
|
|
5805
|
+
const style = {
|
|
5806
|
+
transform: CSS.Transform.toString(transform),
|
|
5807
|
+
transition,
|
|
5808
|
+
opacity: isDragging ? 0.4 : 1
|
|
5809
|
+
};
|
|
5810
|
+
return /* @__PURE__ */ jsx92("div", { ref: setNodeRef, style, ...attributes, ...listeners, children: /* @__PURE__ */ jsx92(KanbanCardView, { card }) });
|
|
5811
|
+
}
|
|
5812
|
+
function KanbanCardView({ card }) {
|
|
5813
|
+
return /* @__PURE__ */ jsx92("div", { className: "cursor-grab touch-none rounded-md border bg-background p-3 text-sm text-foreground shadow-sm outline-none active:cursor-grabbing focus-visible:ring-1 focus-visible:ring-ring", children: card.content });
|
|
5814
|
+
}
|
|
5815
|
+
|
|
5816
|
+
// src/components/markdown-editor.tsx
|
|
5817
|
+
import * as React88 from "react";
|
|
5818
|
+
import { Bold, Code, Eye as Eye2, EyeOff as EyeOff2, Heading2, Italic, Link as LinkIcon, List as List4, ListOrdered, Quote as Quote2 } from "lucide-react";
|
|
5819
|
+
import { jsx as jsx93, jsxs as jsxs58 } from "react/jsx-runtime";
|
|
5820
|
+
function wrapSelection(text, start, end, before, after, placeholder) {
|
|
5821
|
+
const selected = text.slice(start, end) || placeholder;
|
|
5822
|
+
const next = text.slice(0, start) + before + selected + after + text.slice(end);
|
|
5823
|
+
return { text: next, selectionStart: start + before.length, selectionEnd: start + before.length + selected.length };
|
|
5824
|
+
}
|
|
5825
|
+
function prefixLines(text, start, end, prefix) {
|
|
5826
|
+
const lineStart = text.lastIndexOf("\n", Math.max(start - 1, 0)) + 1;
|
|
5827
|
+
let lineEnd = text.indexOf("\n", Math.max(end, lineStart));
|
|
5828
|
+
if (lineEnd === -1) lineEnd = text.length;
|
|
5829
|
+
const block = text.slice(lineStart, lineEnd);
|
|
5830
|
+
const lines = block.split("\n");
|
|
5831
|
+
const prefixed = lines.map((line, i) => prefix(i) + line).join("\n");
|
|
5832
|
+
return {
|
|
5833
|
+
text: text.slice(0, lineStart) + prefixed + text.slice(lineEnd),
|
|
5834
|
+
selectionStart: start + prefix(0).length,
|
|
5835
|
+
selectionEnd: end + (prefixed.length - block.length)
|
|
5836
|
+
};
|
|
5837
|
+
}
|
|
5838
|
+
function escapeHtml(s) {
|
|
5839
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
5840
|
+
}
|
|
5841
|
+
function renderInline(escaped) {
|
|
5842
|
+
return escaped.replace(/`([^`]+)`/g, "<code>$1</code>").replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\*([^*]+)\*/g, "<em>$1</em>").replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, label, url) => {
|
|
5843
|
+
const safe = /^(https?:|mailto:|\/|#)/i.test(url.trim());
|
|
5844
|
+
return safe ? `<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>` : match;
|
|
5845
|
+
});
|
|
5846
|
+
}
|
|
5847
|
+
function matchMarker(line, markerRe) {
|
|
5848
|
+
const m = markerRe.exec(line);
|
|
5849
|
+
if (!m) return null;
|
|
5850
|
+
let i = m[0].length;
|
|
5851
|
+
if (line[i] !== " " && line[i] !== " ") return null;
|
|
5852
|
+
while (line[i] === " " || line[i] === " ") i++;
|
|
5853
|
+
return { marker: m[0], content: line.slice(i) };
|
|
5854
|
+
}
|
|
5855
|
+
function renderMarkdown(source) {
|
|
5856
|
+
const lines = source.split("\n");
|
|
5857
|
+
const out = [];
|
|
5858
|
+
let inCode = false;
|
|
5859
|
+
let codeLines = [];
|
|
5860
|
+
let listType = null;
|
|
5861
|
+
const closeList = () => {
|
|
5862
|
+
if (listType) {
|
|
5863
|
+
out.push(`</${listType}>`);
|
|
5864
|
+
listType = null;
|
|
5865
|
+
}
|
|
5866
|
+
};
|
|
5867
|
+
for (const line of lines) {
|
|
5868
|
+
if (line.trim().startsWith("```")) {
|
|
5869
|
+
if (inCode) {
|
|
5870
|
+
out.push(`<pre><code>${escapeHtml(codeLines.join("\n"))}</code></pre>`);
|
|
5871
|
+
codeLines = [];
|
|
5872
|
+
inCode = false;
|
|
5873
|
+
} else {
|
|
5874
|
+
closeList();
|
|
5875
|
+
inCode = true;
|
|
5876
|
+
}
|
|
5877
|
+
continue;
|
|
5878
|
+
}
|
|
5879
|
+
if (inCode) {
|
|
5880
|
+
codeLines.push(line);
|
|
5881
|
+
continue;
|
|
5882
|
+
}
|
|
5883
|
+
const heading = matchMarker(line, /^#{1,6}/);
|
|
5884
|
+
if (heading) {
|
|
5885
|
+
closeList();
|
|
5886
|
+
const level = heading.marker.length;
|
|
5887
|
+
out.push(`<h${level}>${renderInline(escapeHtml(heading.content))}</h${level}>`);
|
|
5888
|
+
continue;
|
|
5889
|
+
}
|
|
5890
|
+
const quote = line.match(/^>\s?(.*)$/);
|
|
5891
|
+
if (quote) {
|
|
5892
|
+
closeList();
|
|
5893
|
+
out.push(`<blockquote>${renderInline(escapeHtml(quote[1]))}</blockquote>`);
|
|
5894
|
+
continue;
|
|
5895
|
+
}
|
|
5896
|
+
const bullet = matchMarker(line, /^[-*]/);
|
|
5897
|
+
if (bullet) {
|
|
5898
|
+
if (listType !== "ul") {
|
|
5899
|
+
closeList();
|
|
5900
|
+
out.push("<ul>");
|
|
5901
|
+
listType = "ul";
|
|
5902
|
+
}
|
|
5903
|
+
out.push(`<li>${renderInline(escapeHtml(bullet.content))}</li>`);
|
|
5904
|
+
continue;
|
|
5905
|
+
}
|
|
5906
|
+
const numbered = matchMarker(line, /^\d+\./);
|
|
5907
|
+
if (numbered) {
|
|
5908
|
+
if (listType !== "ol") {
|
|
5909
|
+
closeList();
|
|
5910
|
+
out.push("<ol>");
|
|
5911
|
+
listType = "ol";
|
|
5912
|
+
}
|
|
5913
|
+
out.push(`<li>${renderInline(escapeHtml(numbered.content))}</li>`);
|
|
5914
|
+
continue;
|
|
5915
|
+
}
|
|
5916
|
+
closeList();
|
|
5917
|
+
if (line.trim() === "") continue;
|
|
5918
|
+
out.push(`<p>${renderInline(escapeHtml(line))}</p>`);
|
|
5919
|
+
}
|
|
5920
|
+
closeList();
|
|
5921
|
+
if (inCode) out.push(`<pre><code>${escapeHtml(codeLines.join("\n"))}</code></pre>`);
|
|
5922
|
+
return out.join("\n");
|
|
5923
|
+
}
|
|
5924
|
+
var MarkdownEditor = React88.forwardRef(
|
|
5925
|
+
({ value, onChange, placeholder, rows = 10, className, ...props }, ref) => {
|
|
5926
|
+
const textareaRef = React88.useRef(null);
|
|
5927
|
+
const [showPreview, setShowPreview] = React88.useState(false);
|
|
5928
|
+
const pendingSelection = React88.useRef(null);
|
|
5929
|
+
React88.useEffect(() => {
|
|
5930
|
+
if (pendingSelection.current && textareaRef.current) {
|
|
5931
|
+
const { start, end } = pendingSelection.current;
|
|
5932
|
+
textareaRef.current.focus();
|
|
5933
|
+
textareaRef.current.setSelectionRange(start, end);
|
|
5934
|
+
pendingSelection.current = null;
|
|
5935
|
+
}
|
|
5936
|
+
}, [value]);
|
|
5937
|
+
const applyInline = (before, after, placeholder2) => {
|
|
5938
|
+
const el = textareaRef.current;
|
|
5939
|
+
if (!el) return;
|
|
5940
|
+
const result = wrapSelection(value, el.selectionStart, el.selectionEnd, before, after, placeholder2);
|
|
5941
|
+
pendingSelection.current = { start: result.selectionStart, end: result.selectionEnd };
|
|
5942
|
+
onChange(result.text);
|
|
5943
|
+
};
|
|
5944
|
+
const applyLinePrefix = (prefix) => {
|
|
5945
|
+
const el = textareaRef.current;
|
|
5946
|
+
if (!el) return;
|
|
5947
|
+
const result = prefixLines(value, el.selectionStart, el.selectionEnd, prefix);
|
|
5948
|
+
pendingSelection.current = { start: result.selectionStart, end: result.selectionEnd };
|
|
5949
|
+
onChange(result.text);
|
|
5950
|
+
};
|
|
5951
|
+
return /* @__PURE__ */ jsxs58("div", { ref, className: cn("overflow-hidden rounded-md border font-sans", className), ...props, children: [
|
|
5952
|
+
/* @__PURE__ */ jsxs58("div", { role: "toolbar", "aria-label": "Formatting", className: "flex items-center gap-0.5 border-b bg-muted/40 p-1", children: [
|
|
5953
|
+
/* @__PURE__ */ jsx93(ToolbarButton, { label: "Bold", onClick: () => applyInline("**", "**", "bold text"), children: /* @__PURE__ */ jsx93(Bold, { className: "size-4" }) }),
|
|
5954
|
+
/* @__PURE__ */ jsx93(ToolbarButton, { label: "Italic", onClick: () => applyInline("*", "*", "italic text"), children: /* @__PURE__ */ jsx93(Italic, { className: "size-4" }) }),
|
|
5955
|
+
/* @__PURE__ */ jsx93(ToolbarButton, { label: "Heading", onClick: () => applyLinePrefix(() => "## "), children: /* @__PURE__ */ jsx93(Heading2, { className: "size-4" }) }),
|
|
5956
|
+
/* @__PURE__ */ jsx93(ToolbarButton, { label: "Link", onClick: () => applyInline("[", "](https://)", "link text"), children: /* @__PURE__ */ jsx93(LinkIcon, { className: "size-4" }) }),
|
|
5957
|
+
/* @__PURE__ */ jsx93(ToolbarButton, { label: "Bulleted list", onClick: () => applyLinePrefix(() => "- "), children: /* @__PURE__ */ jsx93(List4, { className: "size-4" }) }),
|
|
5958
|
+
/* @__PURE__ */ jsx93(ToolbarButton, { label: "Numbered list", onClick: () => applyLinePrefix((i) => `${i + 1}. `), children: /* @__PURE__ */ jsx93(ListOrdered, { className: "size-4" }) }),
|
|
5959
|
+
/* @__PURE__ */ jsx93(ToolbarButton, { label: "Code", onClick: () => applyInline("`", "`", "code"), children: /* @__PURE__ */ jsx93(Code, { className: "size-4" }) }),
|
|
5960
|
+
/* @__PURE__ */ jsx93(ToolbarButton, { label: "Quote", onClick: () => applyLinePrefix(() => "> "), children: /* @__PURE__ */ jsx93(Quote2, { className: "size-4" }) }),
|
|
5961
|
+
/* @__PURE__ */ jsx93(
|
|
5962
|
+
ToolbarButton,
|
|
5963
|
+
{
|
|
5964
|
+
label: showPreview ? "Hide preview" : "Show preview",
|
|
5965
|
+
onClick: () => setShowPreview((v) => !v),
|
|
5966
|
+
active: showPreview,
|
|
5967
|
+
className: "ml-auto",
|
|
5968
|
+
children: showPreview ? /* @__PURE__ */ jsx93(EyeOff2, { className: "size-4" }) : /* @__PURE__ */ jsx93(Eye2, { className: "size-4" })
|
|
5969
|
+
}
|
|
5970
|
+
)
|
|
5971
|
+
] }),
|
|
5972
|
+
/* @__PURE__ */ jsxs58("div", { className: cn("grid", showPreview && "grid-cols-2 divide-x"), children: [
|
|
5973
|
+
/* @__PURE__ */ jsx93(
|
|
5974
|
+
Textarea,
|
|
5975
|
+
{
|
|
5976
|
+
ref: textareaRef,
|
|
5977
|
+
value,
|
|
5978
|
+
onChange: (e) => onChange(e.target.value),
|
|
5979
|
+
placeholder,
|
|
5980
|
+
rows,
|
|
5981
|
+
className: "resize-none rounded-none border-0 font-mono text-[13px] focus-visible:border-transparent focus-visible:shadow-none"
|
|
5982
|
+
}
|
|
5983
|
+
),
|
|
5984
|
+
showPreview && /* @__PURE__ */ jsx93(
|
|
5985
|
+
"div",
|
|
5986
|
+
{
|
|
5987
|
+
className: "overflow-auto p-3 text-sm [&_a]:text-primary [&_a]:underline [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground [&_code]:rounded-sm [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.9em] [&_h1]:text-xl [&_h1]:font-semibold [&_h2]:text-lg [&_h2]:font-semibold [&_h3]:font-semibold [&_li]:ml-5 [&_ol]:list-decimal [&_p]:mb-2 [&_pre]:overflow-auto [&_pre]:rounded-sm [&_pre]:bg-muted [&_pre]:p-2 [&_ul]:list-disc",
|
|
5988
|
+
dangerouslySetInnerHTML: { __html: renderMarkdown(value) }
|
|
5989
|
+
}
|
|
5990
|
+
)
|
|
5991
|
+
] })
|
|
5992
|
+
] });
|
|
5993
|
+
}
|
|
5994
|
+
);
|
|
5995
|
+
MarkdownEditor.displayName = "MarkdownEditor";
|
|
5996
|
+
function ToolbarButton({
|
|
5997
|
+
label,
|
|
5998
|
+
onClick,
|
|
5999
|
+
active,
|
|
6000
|
+
className,
|
|
6001
|
+
children
|
|
6002
|
+
}) {
|
|
6003
|
+
return /* @__PURE__ */ jsx93(
|
|
6004
|
+
"button",
|
|
6005
|
+
{
|
|
6006
|
+
type: "button",
|
|
6007
|
+
onClick,
|
|
6008
|
+
"aria-label": label,
|
|
6009
|
+
"aria-pressed": active,
|
|
6010
|
+
className: cn(
|
|
6011
|
+
"inline-flex size-7 items-center justify-center rounded-sm text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
6012
|
+
active && "bg-accent text-foreground",
|
|
6013
|
+
className
|
|
6014
|
+
),
|
|
6015
|
+
children
|
|
6016
|
+
}
|
|
6017
|
+
);
|
|
6018
|
+
}
|
|
4209
6019
|
export {
|
|
4210
6020
|
Accordion,
|
|
4211
6021
|
AccordionContent,
|
|
@@ -4273,6 +6083,7 @@ export {
|
|
|
4273
6083
|
Collapsible,
|
|
4274
6084
|
CollapsibleContent2 as CollapsibleContent,
|
|
4275
6085
|
CollapsibleTrigger2 as CollapsibleTrigger,
|
|
6086
|
+
ColorPicker,
|
|
4276
6087
|
Command,
|
|
4277
6088
|
CommandDialog,
|
|
4278
6089
|
CommandEmpty,
|
|
@@ -4282,6 +6093,7 @@ export {
|
|
|
4282
6093
|
CommandList,
|
|
4283
6094
|
CommandSeparator,
|
|
4284
6095
|
CommandShortcut,
|
|
6096
|
+
ComparisonSlider,
|
|
4285
6097
|
ContextMenu,
|
|
4286
6098
|
ContextMenuCheckboxItem,
|
|
4287
6099
|
ContextMenuContent,
|
|
@@ -4297,6 +6109,7 @@ export {
|
|
|
4297
6109
|
ContextMenuSubContent,
|
|
4298
6110
|
ContextMenuSubTrigger,
|
|
4299
6111
|
ContextMenuTrigger,
|
|
6112
|
+
DataGrid,
|
|
4300
6113
|
DataTable,
|
|
4301
6114
|
DatePicker,
|
|
4302
6115
|
DescriptionList,
|
|
@@ -4311,6 +6124,7 @@ export {
|
|
|
4311
6124
|
DialogPortal,
|
|
4312
6125
|
DialogTitle,
|
|
4313
6126
|
DialogTrigger,
|
|
6127
|
+
DiffViewer,
|
|
4314
6128
|
Drawer,
|
|
4315
6129
|
DrawerClose,
|
|
4316
6130
|
DrawerContent,
|
|
@@ -4376,11 +6190,15 @@ export {
|
|
|
4376
6190
|
InputOTPGroup,
|
|
4377
6191
|
InputOTPSeparator,
|
|
4378
6192
|
InputOTPSlot,
|
|
6193
|
+
JsonViewer,
|
|
6194
|
+
KanbanBoard,
|
|
4379
6195
|
Kbd,
|
|
4380
6196
|
KbdGroup,
|
|
4381
6197
|
Label2 as Label,
|
|
4382
6198
|
List3 as List,
|
|
4383
6199
|
ListItem,
|
|
6200
|
+
MarkdownEditor,
|
|
6201
|
+
Marquee,
|
|
4384
6202
|
Menubar,
|
|
4385
6203
|
MenubarCheckboxItem,
|
|
4386
6204
|
MenubarContent,
|
|
@@ -4397,8 +6215,10 @@ export {
|
|
|
4397
6215
|
MenubarSubContent,
|
|
4398
6216
|
MenubarSubTrigger,
|
|
4399
6217
|
MenubarTrigger,
|
|
6218
|
+
MessageBubble,
|
|
4400
6219
|
Metric,
|
|
4401
6220
|
Modal,
|
|
6221
|
+
MultiSelect,
|
|
4402
6222
|
NativeSelect,
|
|
4403
6223
|
NativeSelectOptGroup,
|
|
4404
6224
|
NativeSelectOption,
|
|
@@ -4411,7 +6231,12 @@ export {
|
|
|
4411
6231
|
NavigationMenuList,
|
|
4412
6232
|
NavigationMenuTrigger,
|
|
4413
6233
|
NavigationMenuViewport,
|
|
6234
|
+
NotificationCenter,
|
|
6235
|
+
NotificationCenterContent,
|
|
6236
|
+
NotificationCenterTrigger,
|
|
6237
|
+
NotificationItem,
|
|
4414
6238
|
NumberInput,
|
|
6239
|
+
PageHeader,
|
|
4415
6240
|
Pagination,
|
|
4416
6241
|
PaginationContent,
|
|
4417
6242
|
PaginationEllipsis,
|
|
@@ -4502,6 +6327,11 @@ export {
|
|
|
4502
6327
|
TooltipContent,
|
|
4503
6328
|
TooltipProvider,
|
|
4504
6329
|
TooltipTrigger,
|
|
6330
|
+
Tour,
|
|
6331
|
+
TreeItem,
|
|
6332
|
+
TreeView,
|
|
6333
|
+
TypingIndicator,
|
|
6334
|
+
VirtualList,
|
|
4505
6335
|
badgeVariants,
|
|
4506
6336
|
bannerVariants,
|
|
4507
6337
|
buttonVariants,
|