@unicitylabs/sphere-ui 0.1.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.js ADDED
@@ -0,0 +1,1095 @@
1
+ // src/components/Input.tsx
2
+ import { forwardRef } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ var inputBase = "w-full px-4 py-2.5 bg-neutral-100 dark:bg-white/6 text-neutral-900 dark:text-white placeholder-neutral-400 dark:placeholder-white/25 rounded-xl border border-neutral-200 dark:border-white/8 focus:outline-none focus:border-orange-500 dark:focus:border-brand-orange transition-colors disabled:opacity-50";
5
+ var inputError = "border-red-400 dark:border-red-500/50 focus:border-red-500";
6
+ var Input = forwardRef(
7
+ ({ className = "", error, ...props }, ref) => /* @__PURE__ */ jsx(
8
+ "input",
9
+ {
10
+ ref,
11
+ className: `${inputBase} ${error ? inputError : ""} ${className}`,
12
+ ...props
13
+ }
14
+ )
15
+ );
16
+ Input.displayName = "Input";
17
+ var Textarea = forwardRef(
18
+ ({ className = "", error, ...props }, ref) => /* @__PURE__ */ jsx(
19
+ "textarea",
20
+ {
21
+ ref,
22
+ className: `${inputBase} min-h-20 resize-y ${error ? inputError : ""} ${className}`,
23
+ ...props
24
+ }
25
+ )
26
+ );
27
+ Textarea.displayName = "Textarea";
28
+ var Select = forwardRef(
29
+ ({ className = "", error, children, ...props }, ref) => /* @__PURE__ */ jsx(
30
+ "select",
31
+ {
32
+ ref,
33
+ className: `${inputBase} cursor-pointer appearance-none bg-[length:16px] bg-[right_8px_center] bg-no-repeat ${error ? inputError : ""} ${className}`,
34
+ style: {
35
+ backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.28)' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`,
36
+ paddingRight: "2rem"
37
+ },
38
+ ...props,
39
+ children
40
+ }
41
+ )
42
+ );
43
+ Select.displayName = "Select";
44
+ var buttonVariants = {
45
+ primary: "bg-orange-500 dark:bg-brand-orange hover:bg-orange-600 dark:hover:bg-brand-orange-dark text-white shadow-md shadow-orange-500/15 hover:shadow-lg hover:shadow-orange-500/20 active:translate-y-0 hover:-translate-y-px",
46
+ secondary: "bg-neutral-100 dark:bg-white/6 text-neutral-700 dark:text-white/70 border border-neutral-200 dark:border-white/8 hover:bg-neutral-200 dark:hover:bg-white/10 hover:text-neutral-900 dark:hover:text-white",
47
+ danger: "bg-red-500/10 text-red-500 dark:text-red-400 border border-red-500/20 hover:bg-red-500/20",
48
+ ghost: "text-neutral-500 dark:text-white/45 hover:text-neutral-900 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-white/6"
49
+ };
50
+ var Button = forwardRef(
51
+ ({ variant = "primary", icon, children, className = "", disabled, ...props }, ref) => /* @__PURE__ */ jsxs(
52
+ "button",
53
+ {
54
+ ref,
55
+ disabled,
56
+ className: `inline-flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl text-sm font-semibold transition-all cursor-pointer disabled:opacity-40 disabled:pointer-events-none ${buttonVariants[variant]} ${className}`,
57
+ ...props,
58
+ children: [
59
+ icon,
60
+ children
61
+ ]
62
+ }
63
+ )
64
+ );
65
+ Button.displayName = "Button";
66
+
67
+ // src/components/Field.tsx
68
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
69
+ function Field({ label, required, error, hint, className, children }) {
70
+ return /* @__PURE__ */ jsxs2("div", { className, children: [
71
+ /* @__PURE__ */ jsxs2("label", { className: "text-xs block mb-1", style: { color: "var(--text-muted)" }, children: [
72
+ label,
73
+ required && /* @__PURE__ */ jsx2("span", { className: "text-red-400 ml-0.5", children: "*" })
74
+ ] }),
75
+ children,
76
+ hint && /* @__PURE__ */ jsx2("p", { className: "text-[10px] mt-1", style: { color: "var(--text-muted)" }, children: hint }),
77
+ error && /* @__PURE__ */ jsx2("p", { className: "text-xs text-red-400 mt-1", children: error })
78
+ ] });
79
+ }
80
+ function Section({ title, children }) {
81
+ return /* @__PURE__ */ jsxs2("div", { children: [
82
+ /* @__PURE__ */ jsx2("h2", { className: "text-sm font-semibold mb-3 pb-2 border-b", style: { color: "var(--text-secondary)", borderColor: "var(--border)" }, children: title }),
83
+ children
84
+ ] });
85
+ }
86
+
87
+ // src/components/FormModal.tsx
88
+ import { useEffect } from "react";
89
+ import { createPortal } from "react-dom";
90
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
91
+ function FormModal({ title, isOpen, onClose, children, maxWidth = "max-w-lg" }) {
92
+ useEffect(() => {
93
+ if (!isOpen) return;
94
+ const handler = (e) => {
95
+ if (e.key === "Escape") onClose();
96
+ };
97
+ document.addEventListener("keydown", handler);
98
+ return () => document.removeEventListener("keydown", handler);
99
+ }, [isOpen, onClose]);
100
+ if (!isOpen) return null;
101
+ return createPortal(
102
+ /* @__PURE__ */ jsx3("div", { className: "fixed inset-0 flex items-start justify-center z-50 p-4 overflow-y-auto", style: { background: "rgba(0,0,0,0.7)" }, children: /* @__PURE__ */ jsxs3(
103
+ "div",
104
+ {
105
+ className: `w-full ${maxWidth} mt-12 mb-12 animate-fade-in`,
106
+ style: {
107
+ background: "#111111",
108
+ border: "1px solid var(--border)",
109
+ borderRadius: "var(--radius-lg)",
110
+ boxShadow: "var(--shadow-lg)",
111
+ padding: "1.5rem"
112
+ },
113
+ children: [
114
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-5", children: [
115
+ /* @__PURE__ */ jsx3("h2", { style: { fontFamily: "var(--font-display)", fontSize: "1.05rem", letterSpacing: "0.04em", color: "var(--text-primary)" }, children: title }),
116
+ /* @__PURE__ */ jsx3(
117
+ "button",
118
+ {
119
+ onClick: onClose,
120
+ className: "text-xl leading-none px-2 transition-colors",
121
+ style: { color: "var(--text-muted)" },
122
+ onMouseEnter: (e) => e.currentTarget.style.color = "var(--text-primary)",
123
+ onMouseLeave: (e) => e.currentTarget.style.color = "var(--text-muted)",
124
+ children: "\xD7"
125
+ }
126
+ )
127
+ ] }),
128
+ children
129
+ ]
130
+ }
131
+ ) }),
132
+ document.body
133
+ );
134
+ }
135
+
136
+ // src/components/ConfirmDialog.tsx
137
+ import { useEffect as useEffect2 } from "react";
138
+ import { createPortal as createPortal2 } from "react-dom";
139
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
140
+ function ConfirmDialog({
141
+ isOpen,
142
+ title,
143
+ message,
144
+ confirmLabel = "Confirm",
145
+ variant = "default",
146
+ onConfirm,
147
+ onCancel
148
+ }) {
149
+ useEffect2(() => {
150
+ if (!isOpen) return;
151
+ const handler = (e) => {
152
+ if (e.key === "Escape") onCancel();
153
+ };
154
+ document.addEventListener("keydown", handler);
155
+ return () => document.removeEventListener("keydown", handler);
156
+ }, [isOpen, onCancel]);
157
+ if (!isOpen) return null;
158
+ return createPortal2(
159
+ /* @__PURE__ */ jsx4("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", style: { background: "rgba(0,0,0,0.7)" }, children: /* @__PURE__ */ jsxs4("div", { className: "admin-card p-6 w-full max-w-sm animate-fade-in", children: [
160
+ /* @__PURE__ */ jsx4("h3", { className: "font-semibold text-white mb-2", children: title }),
161
+ /* @__PURE__ */ jsx4("p", { className: "text-sm mb-5", style: { color: "var(--text-muted)" }, children: message }),
162
+ /* @__PURE__ */ jsxs4("div", { className: "flex gap-3", children: [
163
+ /* @__PURE__ */ jsx4("button", { onClick: onCancel, className: "btn-secondary flex-1", children: "Cancel" }),
164
+ /* @__PURE__ */ jsx4(
165
+ "button",
166
+ {
167
+ onClick: onConfirm,
168
+ className: `flex-1 ${variant === "danger" ? "btn-danger" : "btn-primary"}`,
169
+ children: confirmLabel
170
+ }
171
+ )
172
+ ] })
173
+ ] }) }),
174
+ document.body
175
+ );
176
+ }
177
+
178
+ // src/components/StatusBadge.tsx
179
+ import { jsx as jsx5 } from "react/jsx-runtime";
180
+ var STATUS_COLORS = {
181
+ ACTIVE: "badge-green",
182
+ DRAFT: "badge-gray",
183
+ PAUSED: "badge-yellow",
184
+ EXPIRED: "badge-red",
185
+ ENDED: "badge-red",
186
+ AWARDED: "badge-green",
187
+ REJECTED: "badge-red"
188
+ };
189
+ function StatusBadge({ status, className = "" }) {
190
+ return /* @__PURE__ */ jsx5("span", { className: `badge ${STATUS_COLORS[status] ?? "badge-gray"} ${className}`, children: status });
191
+ }
192
+
193
+ // src/components/SearchInput.tsx
194
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
195
+ function SearchInput({ value, onChange, placeholder = "Search..." }) {
196
+ return /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
197
+ /* @__PURE__ */ jsxs5(
198
+ "svg",
199
+ {
200
+ width: "16",
201
+ height: "16",
202
+ viewBox: "0 0 24 24",
203
+ fill: "none",
204
+ stroke: "currentColor",
205
+ strokeWidth: "2",
206
+ strokeLinecap: "round",
207
+ strokeLinejoin: "round",
208
+ className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4",
209
+ style: { color: "var(--text-muted)" },
210
+ children: [
211
+ /* @__PURE__ */ jsx6("circle", { cx: "11", cy: "11", r: "8" }),
212
+ /* @__PURE__ */ jsx6("path", { d: "m21 21-4.35-4.35" })
213
+ ]
214
+ }
215
+ ),
216
+ /* @__PURE__ */ jsx6(
217
+ "input",
218
+ {
219
+ type: "text",
220
+ className: "admin-input !pl-9 !pr-8",
221
+ value,
222
+ onChange: (e) => onChange(e.target.value),
223
+ placeholder
224
+ }
225
+ ),
226
+ value && /* @__PURE__ */ jsx6(
227
+ "button",
228
+ {
229
+ onClick: () => onChange(""),
230
+ className: "absolute right-2 top-1/2 -translate-y-1/2 text-sm px-1",
231
+ style: { color: "var(--text-muted)" },
232
+ onMouseEnter: (e) => e.currentTarget.style.color = "var(--text-primary)",
233
+ onMouseLeave: (e) => e.currentTarget.style.color = "var(--text-muted)",
234
+ children: "\xD7"
235
+ }
236
+ )
237
+ ] });
238
+ }
239
+
240
+ // src/components/EmptyState.tsx
241
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
242
+ function EmptyState({ title, description, action }) {
243
+ return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
244
+ /* @__PURE__ */ jsx7(
245
+ "div",
246
+ {
247
+ className: "w-12 h-12 rounded-xl flex items-center justify-center mb-4",
248
+ style: { background: "var(--bg-elevated)", border: "1px solid var(--border)" },
249
+ children: /* @__PURE__ */ jsx7("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", style: { color: "var(--text-muted)" }, children: /* @__PURE__ */ jsx7("path", { d: "M20 13V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7m16 0v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-5m16 0h-2.586a1 1 0 0 0-.707.293l-2.414 2.414a1 1 0 0 1-.707.293h-3.172a1 1 0 0 1-.707-.293l-2.414-2.414A1 1 0 0 0 6.586 13H4" }) })
250
+ }
251
+ ),
252
+ /* @__PURE__ */ jsx7("div", { className: "text-sm font-medium mb-1", style: { color: "var(--text-secondary)" }, children: title }),
253
+ description && /* @__PURE__ */ jsx7("div", { className: "text-xs mb-4", style: { color: "var(--text-muted)" }, children: description }),
254
+ action
255
+ ] });
256
+ }
257
+
258
+ // src/components/CustomSelect.tsx
259
+ import { useState, useRef, useEffect as useEffect3 } from "react";
260
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
261
+ function CustomSelect({
262
+ options,
263
+ value,
264
+ onChange,
265
+ placeholder,
266
+ className = "",
267
+ size = "md"
268
+ }) {
269
+ const [open, setOpen] = useState(false);
270
+ const ref = useRef(null);
271
+ const selected = options.find((o) => o.value === value);
272
+ const label = selected?.label ?? placeholder ?? "Select...";
273
+ useEffect3(() => {
274
+ if (!open) return;
275
+ const handler = (e) => {
276
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
277
+ };
278
+ document.addEventListener("mousedown", handler);
279
+ return () => document.removeEventListener("mousedown", handler);
280
+ }, [open]);
281
+ useEffect3(() => {
282
+ if (!open) return;
283
+ const handler = (e) => {
284
+ if (e.key === "Escape") setOpen(false);
285
+ };
286
+ document.addEventListener("keydown", handler);
287
+ return () => document.removeEventListener("keydown", handler);
288
+ }, [open]);
289
+ const textSize = size === "sm" ? "text-xs" : "text-sm";
290
+ return /* @__PURE__ */ jsxs7("div", { ref, className: `relative ${className}`, children: [
291
+ /* @__PURE__ */ jsxs7(
292
+ "button",
293
+ {
294
+ type: "button",
295
+ onClick: () => setOpen((o) => !o),
296
+ className: `admin-input w-full flex items-center justify-between gap-2 ${textSize} text-left`,
297
+ style: { color: selected ? "var(--text-primary)" : "var(--text-muted)" },
298
+ children: [
299
+ /* @__PURE__ */ jsx8("span", { className: "truncate", children: label }),
300
+ /* @__PURE__ */ jsx8(
301
+ "svg",
302
+ {
303
+ width: "12",
304
+ height: "12",
305
+ viewBox: "0 0 12 12",
306
+ fill: "none",
307
+ stroke: "currentColor",
308
+ strokeWidth: "1.5",
309
+ strokeLinecap: "round",
310
+ strokeLinejoin: "round",
311
+ className: "flex-shrink-0 transition-transform",
312
+ style: {
313
+ color: "var(--text-muted)",
314
+ transform: open ? "rotate(180deg)" : "rotate(0deg)"
315
+ },
316
+ children: /* @__PURE__ */ jsx8("path", { d: "M3 4.5L6 7.5L9 4.5" })
317
+ }
318
+ )
319
+ ]
320
+ }
321
+ ),
322
+ open && /* @__PURE__ */ jsx8(
323
+ "div",
324
+ {
325
+ className: "absolute left-0 right-0 top-full mt-1 z-30 py-1 max-h-48 overflow-y-auto",
326
+ style: {
327
+ background: "var(--bg-elevated)",
328
+ border: "1px solid var(--border)",
329
+ borderRadius: "var(--radius-md)",
330
+ boxShadow: "0 8px 24px rgba(0,0,0,0.4)"
331
+ },
332
+ children: options.map((opt) => /* @__PURE__ */ jsx8(
333
+ "button",
334
+ {
335
+ type: "button",
336
+ onClick: () => {
337
+ onChange(opt.value);
338
+ setOpen(false);
339
+ },
340
+ className: `block w-full text-left px-3 py-1.5 ${textSize} transition-colors`,
341
+ style: {
342
+ color: opt.value === value ? "var(--accent-text)" : "var(--text-secondary)",
343
+ background: opt.value === value ? "var(--accent-glow)" : "transparent"
344
+ },
345
+ onMouseEnter: (e) => {
346
+ if (opt.value !== value) e.currentTarget.style.background = "var(--bg-hover)";
347
+ },
348
+ onMouseLeave: (e) => {
349
+ e.currentTarget.style.background = opt.value === value ? "var(--accent-glow)" : "transparent";
350
+ },
351
+ children: opt.label
352
+ },
353
+ opt.value
354
+ ))
355
+ }
356
+ )
357
+ ] });
358
+ }
359
+
360
+ // src/components/PageShell.tsx
361
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
362
+ function PageShell({ title, subtitle, action, maxWidth, children }) {
363
+ return /* @__PURE__ */ jsxs8("div", { className: `p-6 lg:p-8 ${maxWidth ?? ""}`, children: [
364
+ /* @__PURE__ */ jsxs8("div", { className: "page-header", children: [
365
+ /* @__PURE__ */ jsxs8("div", { children: [
366
+ /* @__PURE__ */ jsx9("h1", { className: "page-title", children: title }),
367
+ subtitle && /* @__PURE__ */ jsx9("p", { className: "page-subtitle", children: subtitle })
368
+ ] }),
369
+ action
370
+ ] }),
371
+ /* @__PURE__ */ jsx9("div", { className: "animate-fade-in", children })
372
+ ] });
373
+ }
374
+
375
+ // src/components/DataTable.tsx
376
+ import { useState as useState2, useMemo } from "react";
377
+ import {
378
+ useReactTable,
379
+ getCoreRowModel,
380
+ getSortedRowModel,
381
+ getFilteredRowModel,
382
+ flexRender
383
+ } from "@tanstack/react-table";
384
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
385
+ function DataTable({
386
+ columns,
387
+ data,
388
+ isLoading,
389
+ emptyMessage = "No data found",
390
+ searchPlaceholder,
391
+ enableSearch = true,
392
+ onRowClick
393
+ }) {
394
+ const [sorting, setSorting] = useState2([]);
395
+ const [globalFilter, setGlobalFilter] = useState2("");
396
+ const stableColumns = useMemo(() => columns, [columns]);
397
+ const table = useReactTable({
398
+ data,
399
+ columns: stableColumns,
400
+ state: { sorting, globalFilter },
401
+ onSortingChange: setSorting,
402
+ onGlobalFilterChange: setGlobalFilter,
403
+ getCoreRowModel: getCoreRowModel(),
404
+ getSortedRowModel: getSortedRowModel(),
405
+ getFilteredRowModel: getFilteredRowModel()
406
+ });
407
+ if (isLoading) {
408
+ return /* @__PURE__ */ jsx10("div", { className: "py-12 text-center", style: { color: "var(--text-muted)" }, children: "Loading\u2026" });
409
+ }
410
+ return /* @__PURE__ */ jsxs9("div", { children: [
411
+ enableSearch && /* @__PURE__ */ jsx10("div", { className: "mb-4 max-w-xs", children: /* @__PURE__ */ jsx10(
412
+ SearchInput,
413
+ {
414
+ value: globalFilter,
415
+ onChange: setGlobalFilter,
416
+ placeholder: searchPlaceholder ?? "Search..."
417
+ }
418
+ ) }),
419
+ table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx10(EmptyState, { title: emptyMessage }) : /* @__PURE__ */ jsx10("div", { className: "admin-card overflow-hidden", children: /* @__PURE__ */ jsxs9("table", { className: "admin-table", children: [
420
+ /* @__PURE__ */ jsx10("thead", { children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsx10("tr", { children: hg.headers.map((header) => /* @__PURE__ */ jsx10(
421
+ "th",
422
+ {
423
+ className: header.column.getCanSort() ? "cursor-pointer select-none" : "",
424
+ onClick: header.column.getToggleSortingHandler(),
425
+ children: /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1", children: [
426
+ flexRender(header.column.columnDef.header, header.getContext()),
427
+ header.column.getIsSorted() === "asc" && /* @__PURE__ */ jsx10("span", { style: { color: "var(--accent-text)" }, children: "\u25B2" }),
428
+ header.column.getIsSorted() === "desc" && /* @__PURE__ */ jsx10("span", { style: { color: "var(--accent-text)" }, children: "\u25BC" })
429
+ ] })
430
+ },
431
+ header.id
432
+ )) }, hg.id)) }),
433
+ /* @__PURE__ */ jsx10("tbody", { children: table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx10(
434
+ "tr",
435
+ {
436
+ onClick: () => onRowClick?.(row.original),
437
+ className: onRowClick ? "cursor-pointer" : "",
438
+ style: onRowClick ? { transition: "background 0.15s" } : void 0,
439
+ onMouseEnter: (e) => {
440
+ if (onRowClick) e.currentTarget.style.background = "rgba(255,255,255,0.03)";
441
+ },
442
+ onMouseLeave: (e) => {
443
+ if (onRowClick) e.currentTarget.style.background = "";
444
+ },
445
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx10("td", { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
446
+ },
447
+ row.id
448
+ )) })
449
+ ] }) })
450
+ ] });
451
+ }
452
+
453
+ // src/components/AlertBanner.tsx
454
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
455
+ var STYLES = {
456
+ warning: {
457
+ bg: "rgba(251,191,36,0.06)",
458
+ border: "rgba(251,191,36,0.18)",
459
+ icon: "\u26A0\uFE0F",
460
+ titleColor: "#fbbf24"
461
+ },
462
+ info: {
463
+ bg: "rgba(96,165,250,0.06)",
464
+ border: "rgba(96,165,250,0.18)",
465
+ icon: "\u2139\uFE0F",
466
+ titleColor: "#60a5fa"
467
+ }
468
+ };
469
+ function AlertBanner({ type, title, children }) {
470
+ const s = STYLES[type];
471
+ return /* @__PURE__ */ jsx11(
472
+ "div",
473
+ {
474
+ className: "rounded-lg p-3 text-sm",
475
+ style: { background: s.bg, border: `1px solid ${s.border}` },
476
+ children: /* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-2", children: [
477
+ /* @__PURE__ */ jsx11("span", { className: "flex-shrink-0 text-sm leading-5", children: s.icon }),
478
+ /* @__PURE__ */ jsxs10("div", { children: [
479
+ /* @__PURE__ */ jsx11("div", { className: "font-medium text-xs mb-0.5", style: { color: s.titleColor }, children: title }),
480
+ /* @__PURE__ */ jsx11("div", { style: { color: "var(--text-muted)", fontSize: "0.75rem", lineHeight: "1.4" }, children })
481
+ ] })
482
+ ] })
483
+ }
484
+ );
485
+ }
486
+
487
+ // src/components/AddressDisplay.tsx
488
+ import { useState as useState3, useCallback } from "react";
489
+ import { Fragment, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
490
+ function truncateAddress(address) {
491
+ const prefix = "DIRECT://";
492
+ if (address.startsWith(prefix)) {
493
+ const hex = address.slice(prefix.length);
494
+ if (hex.length > 18) {
495
+ return `${prefix}${hex.slice(0, 12)}...${hex.slice(-6)}`;
496
+ }
497
+ }
498
+ if (address.length > 24) {
499
+ return `${address.slice(0, 18)}...${address.slice(-6)}`;
500
+ }
501
+ return address;
502
+ }
503
+ function AddressDisplay({ address, nametag, truncate = true }) {
504
+ const [copied, setCopied] = useState3(false);
505
+ const handleCopy = useCallback(() => {
506
+ navigator.clipboard.writeText(address).then(() => {
507
+ setCopied(true);
508
+ setTimeout(() => setCopied(false), 1500);
509
+ });
510
+ }, [address]);
511
+ const displayAddress = truncate ? truncateAddress(address) : address;
512
+ return /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 min-w-0", children: [
513
+ /* @__PURE__ */ jsx12("div", { className: "min-w-0 flex-1", children: nametag ? /* @__PURE__ */ jsxs11(Fragment, { children: [
514
+ /* @__PURE__ */ jsxs11("div", { className: "text-sm font-medium", style: { color: "var(--text-primary)" }, children: [
515
+ "@",
516
+ nametag
517
+ ] }),
518
+ /* @__PURE__ */ jsx12(
519
+ "div",
520
+ {
521
+ className: "text-[11px] font-mono truncate",
522
+ style: { color: "var(--text-muted)" },
523
+ title: address,
524
+ children: displayAddress
525
+ }
526
+ )
527
+ ] }) : /* @__PURE__ */ jsx12(
528
+ "div",
529
+ {
530
+ className: "text-xs font-mono truncate",
531
+ style: { color: "var(--text-secondary)" },
532
+ title: address,
533
+ children: displayAddress
534
+ }
535
+ ) }),
536
+ /* @__PURE__ */ jsx12(
537
+ "button",
538
+ {
539
+ onClick: handleCopy,
540
+ className: "shrink-0 p-1 rounded transition-colors cursor-pointer",
541
+ style: { color: copied ? "var(--accent-text)" : "var(--text-muted)" },
542
+ onMouseEnter: (e) => {
543
+ if (!copied) e.currentTarget.style.color = "var(--text-secondary)";
544
+ },
545
+ onMouseLeave: (e) => {
546
+ if (!copied) e.currentTarget.style.color = "var(--text-muted)";
547
+ },
548
+ title: copied ? "Copied!" : "Copy address",
549
+ children: copied ? /* @__PURE__ */ jsx12("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx12("path", { d: "M20 6L9 17l-5-5" }) }) : /* @__PURE__ */ jsxs11("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
550
+ /* @__PURE__ */ jsx12("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
551
+ /* @__PURE__ */ jsx12("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
552
+ ] })
553
+ }
554
+ )
555
+ ] });
556
+ }
557
+
558
+ // src/components/JsonPanel.tsx
559
+ import { useState as useState4, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback2 } from "react";
560
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
561
+ function JsonPanel({
562
+ value,
563
+ onChange,
564
+ excludeKeys = [],
565
+ title = "JSON"
566
+ }) {
567
+ const [text, setText] = useState4("");
568
+ const [parseError, setParseError] = useState4(null);
569
+ const [isEditing, setIsEditing] = useState4(false);
570
+ const textareaRef = useRef2(null);
571
+ useEffect4(() => {
572
+ if (isEditing) return;
573
+ const filtered = { ...value };
574
+ for (const key of excludeKeys) {
575
+ delete filtered[key];
576
+ }
577
+ const clean = JSON.parse(JSON.stringify(filtered));
578
+ setText(JSON.stringify(clean, null, 2));
579
+ setParseError(null);
580
+ }, [value, isEditing, excludeKeys]);
581
+ const handleChange = useCallback2((newText) => {
582
+ setText(newText);
583
+ try {
584
+ const parsed = JSON.parse(newText);
585
+ setParseError(null);
586
+ onChange(parsed);
587
+ } catch (e) {
588
+ setParseError(e instanceof Error ? e.message : "Invalid JSON");
589
+ }
590
+ }, [onChange]);
591
+ const handleFocus = useCallback2(() => setIsEditing(true), []);
592
+ const handleBlur = useCallback2(() => setIsEditing(false), []);
593
+ const [copied, setCopied] = useState4(false);
594
+ const handleCopy = useCallback2(async () => {
595
+ await navigator.clipboard.writeText(text);
596
+ setCopied(true);
597
+ setTimeout(() => setCopied(false), 1500);
598
+ }, [text]);
599
+ const lineCount = text.split("\n").length;
600
+ return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col h-full", style: { minWidth: 320 }, children: [
601
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between px-3 py-2", style: { borderBottom: "1px solid var(--border)" }, children: [
602
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
603
+ /* @__PURE__ */ jsxs12("span", { className: "text-xs font-mono font-semibold", style: { color: "var(--text-muted)" }, children: [
604
+ "{ }",
605
+ " ",
606
+ title
607
+ ] }),
608
+ parseError && /* @__PURE__ */ jsx13("span", { className: "text-[10px] px-1.5 py-0.5 rounded", style: { background: "rgba(239,68,68,0.1)", color: "#f87171" }, children: "Error" })
609
+ ] }),
610
+ /* @__PURE__ */ jsx13(
611
+ "button",
612
+ {
613
+ onClick: handleCopy,
614
+ className: "text-[10px] px-2 py-1 rounded transition-colors",
615
+ style: {
616
+ color: copied ? "#34d399" : "var(--text-muted)",
617
+ background: "var(--bg-surface)",
618
+ border: `1px solid ${copied ? "rgba(52,211,153,0.3)" : "var(--border)"}`
619
+ },
620
+ onMouseEnter: (e) => {
621
+ if (!copied) e.currentTarget.style.color = "var(--text-primary)";
622
+ },
623
+ onMouseLeave: (e) => {
624
+ if (!copied) e.currentTarget.style.color = "var(--text-muted)";
625
+ },
626
+ title: "Copy JSON",
627
+ children: copied ? "Copied!" : "Copy"
628
+ }
629
+ )
630
+ ] }),
631
+ /* @__PURE__ */ jsx13("div", { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxs12("div", { className: "absolute inset-0 flex overflow-auto", children: [
632
+ /* @__PURE__ */ jsx13(
633
+ "div",
634
+ {
635
+ className: "shrink-0 text-right pr-2 pt-3 select-none",
636
+ style: {
637
+ fontFamily: "var(--font-mono, monospace)",
638
+ fontSize: "0.7rem",
639
+ lineHeight: "1.35rem",
640
+ color: "var(--text-muted)",
641
+ opacity: 0.4,
642
+ width: 36,
643
+ background: "var(--bg-surface)",
644
+ borderRight: "1px solid var(--border)"
645
+ },
646
+ children: Array.from({ length: lineCount }, (_, i) => /* @__PURE__ */ jsx13("div", { children: i + 1 }, i))
647
+ }
648
+ ),
649
+ /* @__PURE__ */ jsx13(
650
+ "textarea",
651
+ {
652
+ ref: textareaRef,
653
+ value: text,
654
+ onChange: (e) => handleChange(e.target.value),
655
+ onFocus: handleFocus,
656
+ onBlur: handleBlur,
657
+ spellCheck: false,
658
+ className: "flex-1 resize-none outline-none p-3",
659
+ style: {
660
+ fontFamily: "var(--font-mono, monospace)",
661
+ fontSize: "0.7rem",
662
+ lineHeight: "1.35rem",
663
+ color: parseError ? "#f87171" : "var(--text-secondary)",
664
+ background: "transparent",
665
+ border: "none",
666
+ tabSize: 2
667
+ }
668
+ }
669
+ )
670
+ ] }) }),
671
+ parseError && /* @__PURE__ */ jsx13("div", { className: "px-3 py-1.5 text-[10px]", style: { background: "rgba(239,68,68,0.06)", color: "#f87171", borderTop: "1px solid rgba(239,68,68,0.15)" }, children: parseError })
672
+ ] });
673
+ }
674
+ function JsonToggleButton({ active, onClick }) {
675
+ return /* @__PURE__ */ jsx13(
676
+ "button",
677
+ {
678
+ onClick,
679
+ className: "text-xs px-2.5 py-1 rounded transition-all",
680
+ style: {
681
+ fontFamily: "var(--font-mono, monospace)",
682
+ fontWeight: active ? 600 : 400,
683
+ color: active ? "#FF6F00" : "var(--text-muted)",
684
+ background: active ? "rgba(255,111,0,0.08)" : "var(--bg-surface)",
685
+ border: `1px solid ${active ? "rgba(255,111,0,0.2)" : "var(--border)"}`
686
+ },
687
+ onMouseEnter: (e) => {
688
+ if (!active) {
689
+ e.currentTarget.style.color = "var(--text-primary)";
690
+ e.currentTarget.style.borderColor = "var(--text-muted)";
691
+ }
692
+ },
693
+ onMouseLeave: (e) => {
694
+ if (!active) {
695
+ e.currentTarget.style.color = "var(--text-muted)";
696
+ e.currentTarget.style.borderColor = "var(--border)";
697
+ }
698
+ },
699
+ title: active ? "Hide JSON panel" : "Show JSON panel",
700
+ children: "{ }"
701
+ }
702
+ );
703
+ }
704
+
705
+ // src/components/ChainInput.tsx
706
+ import { useState as useState5, useId } from "react";
707
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
708
+ var TAG_PALETTES = [
709
+ { bg: "rgba(16, 185, 129, 0.12)", color: "#34d399", border: "rgba(16, 185, 129, 0.2)" },
710
+ { bg: "rgba(59, 130, 246, 0.12)", color: "#60a5fa", border: "rgba(59, 130, 246, 0.2)" },
711
+ { bg: "rgba(168, 85, 247, 0.12)", color: "#c084fc", border: "rgba(168, 85, 247, 0.2)" },
712
+ { bg: "rgba(6, 182, 212, 0.12)", color: "#22d3ee", border: "rgba(6, 182, 212, 0.2)" },
713
+ { bg: "rgba(245, 158, 11, 0.12)", color: "#fbbf24", border: "rgba(245, 158, 11, 0.2)" },
714
+ { bg: "rgba(244, 63, 94, 0.12)", color: "#fb7185", border: "rgba(244, 63, 94, 0.2)" },
715
+ { bg: "rgba(255, 111, 0, 0.12)", color: "#FF6F00", border: "rgba(255, 111, 0, 0.2)" },
716
+ { bg: "rgba(132, 204, 22, 0.12)", color: "#a3e635", border: "rgba(132, 204, 22, 0.2)" },
717
+ { bg: "rgba(236, 72, 153, 0.12)", color: "#f472b6", border: "rgba(236, 72, 153, 0.2)" },
718
+ { bg: "rgba(99, 102, 241, 0.12)", color: "#818cf8", border: "rgba(99, 102, 241, 0.2)" }
719
+ ];
720
+ function tagColor(tag) {
721
+ let hash = 0;
722
+ for (let i = 0; i < tag.length; i++) hash = (hash << 5) - hash + tag.charCodeAt(i) | 0;
723
+ return TAG_PALETTES[Math.abs(hash) % TAG_PALETTES.length];
724
+ }
725
+ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
726
+ const [input, setInput] = useState5("");
727
+ const [open, setOpen] = useState5(false);
728
+ const inputId = useId();
729
+ const entries = Object.entries(chains);
730
+ const available = suggestions.filter((s) => !(s in chains) && (!input || s.toLowerCase().includes(input.toLowerCase())));
731
+ const trimmed = input.trim().toLowerCase().replace(/\s+/g, "_");
732
+ const isNew = trimmed && !(trimmed in chains) && !available.includes(trimmed);
733
+ const addChain = (name) => {
734
+ const normalized = name.trim().toLowerCase().replace(/\s+/g, "_");
735
+ if (normalized && !(normalized in chains)) {
736
+ onChange({ ...chains, [normalized]: 0 });
737
+ }
738
+ setInput("");
739
+ setOpen(false);
740
+ };
741
+ const removeChain = (name) => {
742
+ const next = { ...chains };
743
+ delete next[name];
744
+ onChange(next);
745
+ };
746
+ const setOrder = (name, order) => {
747
+ onChange({ ...chains, [name]: Math.max(0, order) });
748
+ };
749
+ const handleKeyDown = (e) => {
750
+ if ((e.key === "Enter" || e.key === ",") && input.trim()) {
751
+ e.preventDefault();
752
+ addChain(input);
753
+ }
754
+ if (e.key === "Backspace" && !input && entries.length > 0) {
755
+ removeChain(entries[entries.length - 1][0]);
756
+ }
757
+ };
758
+ const isSm = size === "sm";
759
+ const textSize = isSm ? "text-[0.6rem]" : "text-[0.65rem]";
760
+ const minH = isSm ? "min-h-[32px]" : "min-h-[38px]";
761
+ return /* @__PURE__ */ jsxs13("div", { className: "relative", children: [
762
+ /* @__PURE__ */ jsxs13(
763
+ "div",
764
+ {
765
+ className: `admin-input flex flex-wrap gap-1.5 !p-1.5 ${minH} cursor-text`,
766
+ onClick: () => document.getElementById(inputId)?.focus(),
767
+ children: [
768
+ entries.map(([name, order]) => {
769
+ const c = tagColor(name);
770
+ return /* @__PURE__ */ jsxs13(
771
+ "span",
772
+ {
773
+ className: `inline-flex items-center gap-1 ${textSize} font-semibold px-2 py-0.5 rounded`,
774
+ style: { background: c.bg, color: c.color, border: `1px solid ${c.border}` },
775
+ children: [
776
+ name,
777
+ /* @__PURE__ */ jsx14(
778
+ "input",
779
+ {
780
+ type: "number",
781
+ min: 0,
782
+ value: order,
783
+ onChange: (e) => setOrder(name, Number(e.target.value)),
784
+ className: `w-7 bg-transparent border-none outline-none text-center ${textSize} font-bold`,
785
+ style: { color: c.color },
786
+ onClick: (e) => e.stopPropagation()
787
+ }
788
+ ),
789
+ /* @__PURE__ */ jsx14(
790
+ "button",
791
+ {
792
+ type: "button",
793
+ onClick: (e) => {
794
+ e.stopPropagation();
795
+ removeChain(name);
796
+ },
797
+ className: "leading-none opacity-60 hover:opacity-100",
798
+ children: "\xD7"
799
+ }
800
+ )
801
+ ]
802
+ },
803
+ name
804
+ );
805
+ }),
806
+ /* @__PURE__ */ jsx14(
807
+ "input",
808
+ {
809
+ id: inputId,
810
+ className: `flex-1 min-w-[60px] bg-transparent border-none outline-none ${isSm ? "text-[11px]" : "text-xs"}`,
811
+ style: { color: "var(--text-primary)" },
812
+ placeholder: entries.length === 0 ? "Add to chain..." : "",
813
+ value: input,
814
+ onFocus: () => setOpen(true),
815
+ onBlur: () => setTimeout(() => setOpen(false), 150),
816
+ onChange: (e) => {
817
+ setInput(e.target.value);
818
+ setOpen(true);
819
+ },
820
+ onKeyDown: handleKeyDown
821
+ }
822
+ )
823
+ ]
824
+ }
825
+ ),
826
+ open && (available.length > 0 || isNew) && /* @__PURE__ */ jsxs13(
827
+ "div",
828
+ {
829
+ className: "absolute left-0 right-0 top-full mt-1 z-20 py-1 max-h-40 overflow-y-auto",
830
+ style: { background: "var(--bg-elevated)", border: "1px solid var(--border)", borderRadius: "var(--radius-md)" },
831
+ children: [
832
+ isNew && /* @__PURE__ */ jsxs13(
833
+ "button",
834
+ {
835
+ onMouseDown: () => addChain(trimmed),
836
+ className: "block w-full text-left px-3 py-1.5 text-sm transition-colors",
837
+ style: { color: "var(--accent-text)" },
838
+ onMouseEnter: (e) => {
839
+ e.currentTarget.style.background = "var(--bg-hover)";
840
+ },
841
+ onMouseLeave: (e) => {
842
+ e.currentTarget.style.background = "transparent";
843
+ },
844
+ children: [
845
+ '+ Create "',
846
+ trimmed,
847
+ '"'
848
+ ]
849
+ }
850
+ ),
851
+ available.map((s) => {
852
+ const c = tagColor(s);
853
+ return /* @__PURE__ */ jsxs13(
854
+ "button",
855
+ {
856
+ onMouseDown: () => addChain(s),
857
+ className: "flex items-center gap-2 w-full text-left px-3 py-1.5 text-sm transition-colors",
858
+ style: { color: "var(--text-secondary)" },
859
+ onMouseEnter: (e) => {
860
+ e.currentTarget.style.background = "var(--bg-hover)";
861
+ },
862
+ onMouseLeave: (e) => {
863
+ e.currentTarget.style.background = "transparent";
864
+ },
865
+ children: [
866
+ /* @__PURE__ */ jsx14("span", { className: "w-2 h-2 rounded-full flex-shrink-0", style: { background: c.color } }),
867
+ s
868
+ ]
869
+ },
870
+ s
871
+ );
872
+ })
873
+ ]
874
+ }
875
+ )
876
+ ] });
877
+ }
878
+
879
+ // src/components/MemoConditionsEditor.tsx
880
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
881
+ var MEMO_OPERATORS = [
882
+ { value: "eq", label: "Equals (=)", values: 1 },
883
+ { value: "neq", label: "Not equals (\u2260)", values: 1 },
884
+ { value: "gt", label: "Greater than (>)", values: 1 },
885
+ { value: "gte", label: "Greater or equal (\u2265)", values: 1 },
886
+ { value: "lt", label: "Less than (<)", values: 1 },
887
+ { value: "lte", label: "Less or equal (\u2264)", values: 1 },
888
+ { value: "between", label: "Between", values: 2 },
889
+ { value: "contains", label: "Contains", values: 1 },
890
+ { value: "exists", label: "Exists (any value)", values: 0 },
891
+ { value: "regex", label: "Matches pattern", values: 1 }
892
+ ];
893
+ function conditionPreview(c) {
894
+ const k = c.key || "\u2026";
895
+ const v = c.value || "\u2026";
896
+ switch (c.operator) {
897
+ case "eq":
898
+ return `"${k}" must equal ${v}`;
899
+ case "neq":
900
+ return `"${k}" must not equal ${v}`;
901
+ case "gt":
902
+ return `"${k}" must be > ${v}`;
903
+ case "gte":
904
+ return `"${k}" must be \u2265 ${v}`;
905
+ case "lt":
906
+ return `"${k}" must be < ${v}`;
907
+ case "lte":
908
+ return `"${k}" must be \u2264 ${v}`;
909
+ case "between":
910
+ return `"${k}" must be between ${v} and ${c.value2 || "\u2026"}`;
911
+ case "contains":
912
+ return `"${k}" must contain "${v}"`;
913
+ case "exists":
914
+ return `memo must have field "${k}"`;
915
+ case "regex":
916
+ return `"${k}" must match /${v}/`;
917
+ default:
918
+ return "";
919
+ }
920
+ }
921
+ function MemoConditionsEditor({ conditions, onChange }) {
922
+ const add = () => onChange([...conditions, { key: "", operator: "eq", value: "" }]);
923
+ const remove = (i) => onChange(conditions.filter((_, idx) => idx !== i));
924
+ const update = (i, patch) => onChange(conditions.map((c, idx) => idx === i ? { ...c, ...patch } : c));
925
+ const opMeta = (op) => MEMO_OPERATORS.find((o) => o.value === op) ?? MEMO_OPERATORS[0];
926
+ return /* @__PURE__ */ jsxs14("div", { children: [
927
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 mb-2", children: [
928
+ /* @__PURE__ */ jsx15("span", { className: "text-xs font-medium", style: { color: "var(--text-muted)" }, children: "Memo Conditions" }),
929
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: add, className: "text-[10px] px-2 py-0.5 rounded cursor-pointer", style: { background: "var(--bg-elevated)", color: "var(--accent-text)" }, children: "+ Add" })
930
+ ] }),
931
+ conditions.length > 0 ? /* @__PURE__ */ jsx15("div", { className: "flex flex-col gap-2", children: conditions.map((cond, i) => {
932
+ const meta = opMeta(cond.operator);
933
+ return /* @__PURE__ */ jsxs14("div", { className: "rounded-lg p-2.5", style: { background: "rgba(255,255,255,0.02)", border: "1px solid var(--border)" }, children: [
934
+ /* @__PURE__ */ jsxs14("div", { className: "flex gap-2 items-center", children: [
935
+ /* @__PURE__ */ jsx15(
936
+ "input",
937
+ {
938
+ className: "admin-input flex-1 text-xs",
939
+ placeholder: "field name",
940
+ value: cond.key,
941
+ onChange: (e) => update(i, { key: e.target.value })
942
+ }
943
+ ),
944
+ /* @__PURE__ */ jsx15(
945
+ CustomSelect,
946
+ {
947
+ size: "sm",
948
+ className: "!w-[160px]",
949
+ value: cond.operator,
950
+ onChange: (v) => update(i, { operator: v }),
951
+ options: MEMO_OPERATORS.map((o) => ({ value: o.value, label: o.label }))
952
+ }
953
+ ),
954
+ meta.values >= 1 && /* @__PURE__ */ jsx15(
955
+ "input",
956
+ {
957
+ className: "admin-input flex-1 text-xs",
958
+ placeholder: "value",
959
+ value: cond.value ?? "",
960
+ onChange: (e) => update(i, { value: e.target.value })
961
+ }
962
+ ),
963
+ meta.values === 2 && /* @__PURE__ */ jsxs14(Fragment2, { children: [
964
+ /* @__PURE__ */ jsx15("span", { className: "text-[10px]", style: { color: "var(--text-muted)" }, children: "and" }),
965
+ /* @__PURE__ */ jsx15(
966
+ "input",
967
+ {
968
+ className: "admin-input flex-1 text-xs",
969
+ placeholder: "value 2",
970
+ value: cond.value2 ?? "",
971
+ onChange: (e) => update(i, { value2: e.target.value })
972
+ }
973
+ )
974
+ ] }),
975
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => remove(i), className: "text-red-400 text-sm px-1 cursor-pointer", children: "\xD7" })
976
+ ] }),
977
+ /* @__PURE__ */ jsx15("p", { className: "text-[10px] mt-1.5 ml-0.5", style: { color: "var(--text-muted)", fontStyle: "italic" }, children: conditionPreview(cond) })
978
+ ] }, i);
979
+ }) }) : /* @__PURE__ */ jsx15("p", { className: "text-[10px]", style: { color: "var(--text-muted)" }, children: 'No conditions \u2014 matches any memo transaction. Click "+ Add" to filter.' })
980
+ ] });
981
+ }
982
+
983
+ // src/components/Icons.tsx
984
+ import { jsx as jsx16 } from "react/jsx-runtime";
985
+ function I({ d, size = 16, className, style }) {
986
+ return /* @__PURE__ */ jsx16(
987
+ "svg",
988
+ {
989
+ width: size,
990
+ height: size,
991
+ viewBox: "0 0 24 24",
992
+ fill: "none",
993
+ stroke: "currentColor",
994
+ strokeWidth: "1.5",
995
+ strokeLinecap: "round",
996
+ strokeLinejoin: "round",
997
+ className,
998
+ style,
999
+ children: /* @__PURE__ */ jsx16("path", { d })
1000
+ }
1001
+ );
1002
+ }
1003
+ var P = {
1004
+ // Navigation
1005
+ back: "M19 12H5 M12 19l-7-7 7-7",
1006
+ undo: "M3 7v6h6 M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6.69 3L3 13",
1007
+ // Tabs (matching sidebar style)
1008
+ quests: "M12 2L2 7l10 5 10-5-10-5z M2 17l10 5 10-5 M2 12l10 5 10-5",
1009
+ tracks: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z M4 22v-7",
1010
+ settings: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z",
1011
+ chain: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71 M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
1012
+ plus: "M12 5v14 M5 12h14",
1013
+ // Actions
1014
+ edit: "M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z",
1015
+ trash: "M3 6h18 M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6 M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",
1016
+ x: "M18 6L6 18 M6 6l12 12",
1017
+ check: "M20 6L9 17l-5-5",
1018
+ search: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z M21 21l-4.35-4.35",
1019
+ // Arrows
1020
+ chevronUp: "M18 15l-6-6-6 6",
1021
+ chevronDown: "M6 9l6 6 6-6",
1022
+ chevronsDown: "M7 13l5 5 5-5 M7 6l5 5 5-5",
1023
+ chevronsRight: "M13 17l5-5-5-5 M6 17l5-5-5-5",
1024
+ arrowRight: "M5 12h14 M12 5l7 7-7 7",
1025
+ // Shapes
1026
+ play: "M5 3l14 9-14 9V3z",
1027
+ star: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14l-5-4.87 6.91-1.01L12 2z",
1028
+ diamond: "M2.7 10.3a2.41 2.41 0 0 0 0 3.41l7.59 7.59a2.41 2.41 0 0 0 3.41 0l7.59-7.59a2.41 2.41 0 0 0 0-3.41L13.7 2.71a2.41 2.41 0 0 0-3.41 0L2.7 10.3z",
1029
+ circle: "M12 12m-4 0a4 4 0 1 0 8 0 4 4 0 1 0-8 0"
1030
+ };
1031
+ var IconBack = (p) => /* @__PURE__ */ jsx16(I, { d: P.back, ...p });
1032
+ var IconUndo = (p) => /* @__PURE__ */ jsx16(I, { d: P.undo, ...p });
1033
+ var IconQuests = (p) => /* @__PURE__ */ jsx16(I, { d: P.quests, ...p });
1034
+ var IconTracks = (p) => /* @__PURE__ */ jsx16(I, { d: P.tracks, ...p });
1035
+ var IconSettings = (p) => /* @__PURE__ */ jsx16(I, { d: P.settings, ...p });
1036
+ var IconChain = (p) => /* @__PURE__ */ jsx16(I, { d: P.chain, ...p });
1037
+ var IconPlus = (p) => /* @__PURE__ */ jsx16(I, { d: P.plus, ...p });
1038
+ var IconEdit = (p) => /* @__PURE__ */ jsx16(I, { d: P.edit, ...p });
1039
+ var IconTrash = (p) => /* @__PURE__ */ jsx16(I, { d: P.trash, ...p });
1040
+ var IconX = (p) => /* @__PURE__ */ jsx16(I, { d: P.x, ...p });
1041
+ var IconCheck = (p) => /* @__PURE__ */ jsx16(I, { d: P.check, ...p });
1042
+ var IconSearch = (p) => /* @__PURE__ */ jsx16(I, { d: P.search, ...p });
1043
+ var IconChevronUp = (p) => /* @__PURE__ */ jsx16(I, { d: P.chevronUp, ...p });
1044
+ var IconChevronDown = (p) => /* @__PURE__ */ jsx16(I, { d: P.chevronDown, ...p });
1045
+ var IconChevronsDown = (p) => /* @__PURE__ */ jsx16(I, { d: P.chevronsDown, ...p });
1046
+ var IconChevronsRight = (p) => /* @__PURE__ */ jsx16(I, { d: P.chevronsRight, ...p });
1047
+ var IconArrowRight = (p) => /* @__PURE__ */ jsx16(I, { d: P.arrowRight, ...p });
1048
+ var IconPlay = (p) => /* @__PURE__ */ jsx16(I, { d: P.play, ...p });
1049
+ var IconStar = (p) => /* @__PURE__ */ jsx16(I, { d: P.star, ...p });
1050
+ var IconDiamond = (p) => /* @__PURE__ */ jsx16(I, { d: P.diamond, ...p });
1051
+ var IconCircle = (p) => /* @__PURE__ */ jsx16(I, { d: P.circle, ...p });
1052
+ export {
1053
+ AddressDisplay,
1054
+ AlertBanner,
1055
+ Button,
1056
+ ChainInput,
1057
+ ConfirmDialog,
1058
+ CustomSelect,
1059
+ DataTable,
1060
+ EmptyState,
1061
+ Field,
1062
+ FormModal,
1063
+ IconArrowRight,
1064
+ IconBack,
1065
+ IconChain,
1066
+ IconCheck,
1067
+ IconChevronDown,
1068
+ IconChevronUp,
1069
+ IconChevronsDown,
1070
+ IconChevronsRight,
1071
+ IconCircle,
1072
+ IconDiamond,
1073
+ IconEdit,
1074
+ IconPlay,
1075
+ IconPlus,
1076
+ IconQuests,
1077
+ IconSearch,
1078
+ IconSettings,
1079
+ IconStar,
1080
+ IconTracks,
1081
+ IconTrash,
1082
+ IconUndo,
1083
+ IconX,
1084
+ Input,
1085
+ JsonPanel,
1086
+ JsonToggleButton,
1087
+ MemoConditionsEditor,
1088
+ PageShell,
1089
+ SearchInput,
1090
+ Section,
1091
+ Select,
1092
+ StatusBadge,
1093
+ Textarea,
1094
+ tagColor
1095
+ };