@unicitylabs/sphere-ui 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,67 @@
1
- import * as react from 'react';
2
- import { ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
3
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode, ButtonHTMLAttributes, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
4
4
  import { ColumnDef } from '@tanstack/react-table';
5
5
  export { A as AchievementData, a as AchievementFormApi, Q as QueryKeys, b as QuestData, c as QuestFormApi, T as TrackData, d as TrackFormApi } from './index-DMHfA7fr.js';
6
6
 
7
+ interface DashboardLayoutProps {
8
+ /** Sidebar header — logo + app name */
9
+ logo: ReactNode;
10
+ /** Navigation content */
11
+ nav: ReactNode;
12
+ /** Footer content (e.g. sign out button) */
13
+ footer?: ReactNode;
14
+ children: ReactNode;
15
+ }
16
+ /**
17
+ * Dashboard shell with sidebar + main content area.
18
+ * Used by sphere-backoffice and sphere-dev.
19
+ */
20
+ declare function DashboardLayout({ logo, nav, footer, children }: DashboardLayoutProps): react_jsx_runtime.JSX.Element;
21
+
22
+ interface AppLogoProps {
23
+ /** Short code displayed in the icon (e.g. "SQ", "SD") */
24
+ icon?: string | ReactNode;
25
+ /** App title (e.g. "SPHERE QUESTS") */
26
+ title: string;
27
+ /** Subtitle (e.g. "ADMIN", "DEVELOPER") */
28
+ subtitle?: string;
29
+ /** Click handler (e.g. navigate to dashboard) */
30
+ onClick?: () => void;
31
+ }
32
+ /**
33
+ * App logo for dashboard sidebar.
34
+ * Orange icon + Anton title + Geist subtitle.
35
+ */
36
+ declare function AppLogo({ icon, title, subtitle, onClick }: AppLogoProps): react_jsx_runtime.JSX.Element;
37
+
38
+ interface NavItem {
39
+ to: string;
40
+ label: string;
41
+ /** SVG path data for the icon (24x24 viewBox) */
42
+ icon?: string;
43
+ /** Custom icon element (overrides icon path) */
44
+ iconElement?: ReactNode;
45
+ /** Badge count (e.g. pending reviews) */
46
+ badge?: number;
47
+ }
48
+ interface NavGroup {
49
+ label?: string;
50
+ items: NavItem[];
51
+ }
52
+ interface SidebarNavProps {
53
+ groups: NavGroup[];
54
+ /** Current path for active state detection */
55
+ currentPath: string;
56
+ /** Called when a nav item is clicked */
57
+ onNavigate: (to: string) => void;
58
+ }
59
+ /**
60
+ * Sidebar navigation with grouped items, icons, and badges.
61
+ * Matches the admin panel sidebar style exactly.
62
+ */
63
+ declare function SidebarNav({ groups, currentPath, onNavigate }: SidebarNavProps): react_jsx_runtime.JSX.Element;
64
+
7
65
  interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
8
66
  error?: boolean;
9
67
  }
@@ -196,4 +254,4 @@ declare const IconStar: (p: IconProps) => react_jsx_runtime.JSX.Element;
196
254
  declare const IconDiamond: (p: IconProps) => react_jsx_runtime.JSX.Element;
197
255
  declare const IconCircle: (p: IconProps) => react_jsx_runtime.JSX.Element;
198
256
 
199
- export { AddressDisplay, AlertBanner, Button, type ButtonProps, type ButtonVariant, ChainInput, ConfirmDialog, CustomSelect, DataTable, EmptyState, Field, FormModal, IconArrowRight, IconBack, IconChain, IconCheck, IconChevronDown, IconChevronUp, IconChevronsDown, IconChevronsRight, IconCircle, IconDiamond, IconEdit, IconPlay, IconPlus, IconQuests, IconSearch, IconSettings, IconStar, IconTracks, IconTrash, IconUndo, IconX, Input, type InputProps, JsonPanel, JsonToggleButton, type MemoCondition, MemoConditionsEditor, PageShell, SearchInput, Section, Select, type SelectOption, type SelectProps, StatusBadge, Textarea, type TextareaProps, tagColor };
257
+ export { AddressDisplay, AlertBanner, AppLogo, Button, type ButtonProps, type ButtonVariant, ChainInput, ConfirmDialog, CustomSelect, DashboardLayout, DataTable, EmptyState, Field, FormModal, IconArrowRight, IconBack, IconChain, IconCheck, IconChevronDown, IconChevronUp, IconChevronsDown, IconChevronsRight, IconCircle, IconDiamond, IconEdit, IconPlay, IconPlus, IconQuests, IconSearch, IconSettings, IconStar, IconTracks, IconTrash, IconUndo, IconX, Input, type InputProps, JsonPanel, JsonToggleButton, type MemoCondition, MemoConditionsEditor, type NavGroup, type NavItem, PageShell, SearchInput, Section, Select, type SelectOption, type SelectProps, SidebarNav, StatusBadge, Textarea, type TextareaProps, tagColor };
package/dist/index.js CHANGED
@@ -1,10 +1,195 @@
1
+ // src/components/DashboardLayout.tsx
2
+ import { useState } from "react";
3
+ import { Menu } from "lucide-react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ function DashboardLayout({ logo, nav, footer, children }) {
6
+ const [mobileOpen, setMobileOpen] = useState(false);
7
+ return /* @__PURE__ */ jsxs("div", { className: "min-h-screen flex", style: { background: "var(--bg-root)" }, children: [
8
+ mobileOpen && /* @__PURE__ */ jsx(
9
+ "div",
10
+ {
11
+ className: "lg:hidden fixed inset-0 bg-black/50 z-40",
12
+ onClick: () => setMobileOpen(false)
13
+ }
14
+ ),
15
+ /* @__PURE__ */ jsxs(
16
+ "aside",
17
+ {
18
+ className: `fixed lg:static inset-y-0 left-0 z-50 w-56 shrink-0 flex flex-col relative transition-transform lg:translate-x-0 ${mobileOpen ? "translate-x-0" : "-translate-x-full"}`,
19
+ style: {
20
+ background: "linear-gradient(180deg, rgba(255,111,0,0.03) 0%, var(--bg-root) 40%)",
21
+ borderRight: "1px solid var(--border)"
22
+ },
23
+ children: [
24
+ /* @__PURE__ */ jsx(
25
+ "div",
26
+ {
27
+ className: "absolute top-0 left-0 right-0 h-px",
28
+ style: { background: "linear-gradient(90deg, transparent, var(--accent), transparent)" }
29
+ }
30
+ ),
31
+ /* @__PURE__ */ jsx("div", { className: "px-5 py-5", style: { borderBottom: "1px solid var(--border)" }, children: logo }),
32
+ /* @__PURE__ */ jsx("nav", { className: "flex-1 px-3 py-3 flex flex-col overflow-y-auto", children: nav }),
33
+ footer && /* @__PURE__ */ jsx("div", { className: "px-3 py-4", style: { borderTop: "1px solid var(--border)" }, children: footer })
34
+ ]
35
+ }
36
+ ),
37
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0", children: [
38
+ /* @__PURE__ */ jsx("div", { className: "lg:hidden flex items-center gap-3 p-4", style: { borderBottom: "1px solid var(--border)" }, children: /* @__PURE__ */ jsx(
39
+ "button",
40
+ {
41
+ onClick: () => setMobileOpen(true),
42
+ style: { color: "var(--text-muted)" },
43
+ children: /* @__PURE__ */ jsx(Menu, { className: "w-5 h-5" })
44
+ }
45
+ ) }),
46
+ /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-auto", children })
47
+ ] })
48
+ ] });
49
+ }
50
+
51
+ // src/components/AppLogo.tsx
52
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
53
+ function AppLogo({ icon, title, subtitle, onClick }) {
54
+ const content = /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2.5", children: [
55
+ /* @__PURE__ */ jsx2(
56
+ "div",
57
+ {
58
+ className: "w-8 h-8 rounded-lg flex items-center justify-center text-white text-xs relative",
59
+ style: {
60
+ background: "var(--accent)",
61
+ boxShadow: "0 0 16px rgba(255, 111, 0, 0.2)",
62
+ fontFamily: "var(--font-display)",
63
+ letterSpacing: "0.04em"
64
+ },
65
+ children: icon ?? title.slice(0, 2)
66
+ }
67
+ ),
68
+ /* @__PURE__ */ jsxs2("div", { children: [
69
+ /* @__PURE__ */ jsx2(
70
+ "div",
71
+ {
72
+ className: "text-sm",
73
+ style: {
74
+ fontFamily: "var(--font-display)",
75
+ color: "var(--text-primary)",
76
+ letterSpacing: "0.04em",
77
+ textTransform: "uppercase"
78
+ },
79
+ children: title
80
+ }
81
+ ),
82
+ subtitle && /* @__PURE__ */ jsx2(
83
+ "div",
84
+ {
85
+ className: "text-[10px] tracking-widest uppercase",
86
+ style: { color: "var(--text-muted)", fontFamily: "var(--font-body)" },
87
+ children: subtitle
88
+ }
89
+ )
90
+ ] })
91
+ ] });
92
+ if (onClick) {
93
+ return /* @__PURE__ */ jsx2("button", { onClick, className: "cursor-pointer text-left", children: content });
94
+ }
95
+ return content;
96
+ }
97
+
98
+ // src/components/SidebarNav.tsx
99
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
100
+ function SvgIcon({ d }) {
101
+ return /* @__PURE__ */ jsx3(
102
+ "svg",
103
+ {
104
+ width: "16",
105
+ height: "16",
106
+ className: "w-4 h-4 shrink-0",
107
+ viewBox: "0 0 24 24",
108
+ fill: "none",
109
+ stroke: "currentColor",
110
+ strokeWidth: "1.5",
111
+ strokeLinecap: "round",
112
+ strokeLinejoin: "round",
113
+ children: /* @__PURE__ */ jsx3("path", { d })
114
+ }
115
+ );
116
+ }
117
+ function SidebarNav({ groups, currentPath, onNavigate }) {
118
+ const isActive = (to) => {
119
+ if (to === "/") return currentPath === "/";
120
+ return currentPath === to || currentPath.startsWith(to + "/");
121
+ };
122
+ return /* @__PURE__ */ jsx3(Fragment, { children: groups.map((group, gi) => /* @__PURE__ */ jsxs3("div", { children: [
123
+ group.label && /* @__PURE__ */ jsx3(
124
+ "div",
125
+ {
126
+ className: "px-3 pt-5 pb-1.5",
127
+ style: {
128
+ fontFamily: "var(--font-body)",
129
+ fontSize: "0.6rem",
130
+ fontWeight: 600,
131
+ letterSpacing: "0.1em",
132
+ textTransform: "uppercase",
133
+ color: "var(--text-muted)"
134
+ },
135
+ children: group.label
136
+ }
137
+ ),
138
+ /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-0.5", children: group.items.map((item) => {
139
+ const active = isActive(item.to);
140
+ return /* @__PURE__ */ jsxs3(
141
+ "button",
142
+ {
143
+ onClick: () => onNavigate(item.to),
144
+ className: "flex items-center gap-3 px-3 py-2 rounded-lg text-[0.8125rem] transition-all w-full text-left",
145
+ style: active ? {
146
+ background: "var(--accent-glow)",
147
+ color: "var(--accent-text)",
148
+ fontWeight: 600,
149
+ fontFamily: "var(--font-body)",
150
+ boxShadow: "inset 2px 0 0 var(--accent)"
151
+ } : {
152
+ color: "var(--text-secondary)",
153
+ fontFamily: "var(--font-body)"
154
+ },
155
+ onMouseEnter: (e) => {
156
+ if (!active) {
157
+ e.currentTarget.style.background = "var(--bg-hover)";
158
+ e.currentTarget.style.color = "var(--text-primary)";
159
+ }
160
+ },
161
+ onMouseLeave: (e) => {
162
+ if (!active) {
163
+ e.currentTarget.style.background = "transparent";
164
+ e.currentTarget.style.color = "var(--text-secondary)";
165
+ }
166
+ },
167
+ children: [
168
+ item.iconElement ?? (item.icon && /* @__PURE__ */ jsx3(SvgIcon, { d: item.icon })),
169
+ item.label,
170
+ item.badge != null && item.badge > 0 && /* @__PURE__ */ jsx3(
171
+ "span",
172
+ {
173
+ className: "ml-auto min-w-5 h-5 flex items-center justify-center rounded-full text-[10px] font-bold text-white px-1.5",
174
+ style: { background: "var(--accent)" },
175
+ children: item.badge
176
+ }
177
+ )
178
+ ]
179
+ },
180
+ item.to
181
+ );
182
+ }) })
183
+ ] }, gi)) });
184
+ }
185
+
1
186
  // src/components/Input.tsx
2
187
  import { forwardRef } from "react";
3
- import { jsx, jsxs } from "react/jsx-runtime";
188
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
4
189
  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
190
  var inputError = "border-red-400 dark:border-red-500/50 focus:border-red-500";
6
191
  var Input = forwardRef(
7
- ({ className = "", error, ...props }, ref) => /* @__PURE__ */ jsx(
192
+ ({ className = "", error, ...props }, ref) => /* @__PURE__ */ jsx4(
8
193
  "input",
9
194
  {
10
195
  ref,
@@ -15,7 +200,7 @@ var Input = forwardRef(
15
200
  );
16
201
  Input.displayName = "Input";
17
202
  var Textarea = forwardRef(
18
- ({ className = "", error, ...props }, ref) => /* @__PURE__ */ jsx(
203
+ ({ className = "", error, ...props }, ref) => /* @__PURE__ */ jsx4(
19
204
  "textarea",
20
205
  {
21
206
  ref,
@@ -26,7 +211,7 @@ var Textarea = forwardRef(
26
211
  );
27
212
  Textarea.displayName = "Textarea";
28
213
  var Select = forwardRef(
29
- ({ className = "", error, children, ...props }, ref) => /* @__PURE__ */ jsx(
214
+ ({ className = "", error, children, ...props }, ref) => /* @__PURE__ */ jsx4(
30
215
  "select",
31
216
  {
32
217
  ref,
@@ -48,7 +233,7 @@ var buttonVariants = {
48
233
  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
234
  };
50
235
  var Button = forwardRef(
51
- ({ variant = "primary", icon, children, className = "", disabled, ...props }, ref) => /* @__PURE__ */ jsxs(
236
+ ({ variant = "primary", icon, children, className = "", disabled, ...props }, ref) => /* @__PURE__ */ jsxs4(
52
237
  "button",
53
238
  {
54
239
  ref,
@@ -65,21 +250,21 @@ var Button = forwardRef(
65
250
  Button.displayName = "Button";
66
251
 
67
252
  // src/components/Field.tsx
68
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
253
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
69
254
  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: [
255
+ return /* @__PURE__ */ jsxs5("div", { className, children: [
256
+ /* @__PURE__ */ jsxs5("label", { className: "text-xs block mb-1", style: { color: "var(--text-muted)" }, children: [
72
257
  label,
73
- required && /* @__PURE__ */ jsx2("span", { className: "text-red-400 ml-0.5", children: "*" })
258
+ required && /* @__PURE__ */ jsx5("span", { className: "text-red-400 ml-0.5", children: "*" })
74
259
  ] }),
75
260
  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 })
261
+ hint && /* @__PURE__ */ jsx5("p", { className: "text-[10px] mt-1", style: { color: "var(--text-muted)" }, children: hint }),
262
+ error && /* @__PURE__ */ jsx5("p", { className: "text-xs text-red-400 mt-1", children: error })
78
263
  ] });
79
264
  }
80
265
  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 }),
266
+ return /* @__PURE__ */ jsxs5("div", { children: [
267
+ /* @__PURE__ */ jsx5("h2", { className: "text-sm font-semibold mb-3 pb-2 border-b", style: { color: "var(--text-secondary)", borderColor: "var(--border)" }, children: title }),
83
268
  children
84
269
  ] });
85
270
  }
@@ -87,7 +272,7 @@ function Section({ title, children }) {
87
272
  // src/components/FormModal.tsx
88
273
  import { useEffect } from "react";
89
274
  import { createPortal } from "react-dom";
90
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
275
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
91
276
  function FormModal({ title, isOpen, onClose, children, maxWidth = "max-w-lg" }) {
92
277
  useEffect(() => {
93
278
  if (!isOpen) return;
@@ -99,7 +284,7 @@ function FormModal({ title, isOpen, onClose, children, maxWidth = "max-w-lg" })
99
284
  }, [isOpen, onClose]);
100
285
  if (!isOpen) return null;
101
286
  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(
287
+ /* @__PURE__ */ jsx6("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__ */ jsxs6(
103
288
  "div",
104
289
  {
105
290
  className: `w-full ${maxWidth} mt-12 mb-12 animate-fade-in`,
@@ -111,9 +296,9 @@ function FormModal({ title, isOpen, onClose, children, maxWidth = "max-w-lg" })
111
296
  padding: "1.5rem"
112
297
  },
113
298
  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(
299
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between mb-5", children: [
300
+ /* @__PURE__ */ jsx6("h2", { style: { fontFamily: "var(--font-display)", fontSize: "1.05rem", letterSpacing: "0.04em", color: "var(--text-primary)" }, children: title }),
301
+ /* @__PURE__ */ jsx6(
117
302
  "button",
118
303
  {
119
304
  onClick: onClose,
@@ -136,7 +321,7 @@ function FormModal({ title, isOpen, onClose, children, maxWidth = "max-w-lg" })
136
321
  // src/components/ConfirmDialog.tsx
137
322
  import { useEffect as useEffect2 } from "react";
138
323
  import { createPortal as createPortal2 } from "react-dom";
139
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
324
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
140
325
  function ConfirmDialog({
141
326
  isOpen,
142
327
  title,
@@ -156,12 +341,12 @@ function ConfirmDialog({
156
341
  }, [isOpen, onCancel]);
157
342
  if (!isOpen) return null;
158
343
  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(
344
+ /* @__PURE__ */ jsx7("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", style: { background: "rgba(0,0,0,0.7)" }, children: /* @__PURE__ */ jsxs7("div", { className: "admin-card p-6 w-full max-w-sm animate-fade-in", children: [
345
+ /* @__PURE__ */ jsx7("h3", { className: "font-semibold text-white mb-2", children: title }),
346
+ /* @__PURE__ */ jsx7("p", { className: "text-sm mb-5", style: { color: "var(--text-muted)" }, children: message }),
347
+ /* @__PURE__ */ jsxs7("div", { className: "flex gap-3", children: [
348
+ /* @__PURE__ */ jsx7("button", { onClick: onCancel, className: "btn-secondary flex-1", children: "Cancel" }),
349
+ /* @__PURE__ */ jsx7(
165
350
  "button",
166
351
  {
167
352
  onClick: onConfirm,
@@ -176,7 +361,7 @@ function ConfirmDialog({
176
361
  }
177
362
 
178
363
  // src/components/StatusBadge.tsx
179
- import { jsx as jsx5 } from "react/jsx-runtime";
364
+ import { jsx as jsx8 } from "react/jsx-runtime";
180
365
  var STATUS_COLORS = {
181
366
  ACTIVE: "badge-green",
182
367
  DRAFT: "badge-gray",
@@ -187,14 +372,14 @@ var STATUS_COLORS = {
187
372
  REJECTED: "badge-red"
188
373
  };
189
374
  function StatusBadge({ status, className = "" }) {
190
- return /* @__PURE__ */ jsx5("span", { className: `badge ${STATUS_COLORS[status] ?? "badge-gray"} ${className}`, children: status });
375
+ return /* @__PURE__ */ jsx8("span", { className: `badge ${STATUS_COLORS[status] ?? "badge-gray"} ${className}`, children: status });
191
376
  }
192
377
 
193
378
  // src/components/SearchInput.tsx
194
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
379
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
195
380
  function SearchInput({ value, onChange, placeholder = "Search..." }) {
196
- return /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
197
- /* @__PURE__ */ jsxs5(
381
+ return /* @__PURE__ */ jsxs8("div", { className: "relative", children: [
382
+ /* @__PURE__ */ jsxs8(
198
383
  "svg",
199
384
  {
200
385
  width: "16",
@@ -208,12 +393,12 @@ function SearchInput({ value, onChange, placeholder = "Search..." }) {
208
393
  className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4",
209
394
  style: { color: "var(--text-muted)" },
210
395
  children: [
211
- /* @__PURE__ */ jsx6("circle", { cx: "11", cy: "11", r: "8" }),
212
- /* @__PURE__ */ jsx6("path", { d: "m21 21-4.35-4.35" })
396
+ /* @__PURE__ */ jsx9("circle", { cx: "11", cy: "11", r: "8" }),
397
+ /* @__PURE__ */ jsx9("path", { d: "m21 21-4.35-4.35" })
213
398
  ]
214
399
  }
215
400
  ),
216
- /* @__PURE__ */ jsx6(
401
+ /* @__PURE__ */ jsx9(
217
402
  "input",
218
403
  {
219
404
  type: "text",
@@ -223,7 +408,7 @@ function SearchInput({ value, onChange, placeholder = "Search..." }) {
223
408
  placeholder
224
409
  }
225
410
  ),
226
- value && /* @__PURE__ */ jsx6(
411
+ value && /* @__PURE__ */ jsx9(
227
412
  "button",
228
413
  {
229
414
  onClick: () => onChange(""),
@@ -238,26 +423,26 @@ function SearchInput({ value, onChange, placeholder = "Search..." }) {
238
423
  }
239
424
 
240
425
  // src/components/EmptyState.tsx
241
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
426
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
242
427
  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(
428
+ return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
429
+ /* @__PURE__ */ jsx10(
245
430
  "div",
246
431
  {
247
432
  className: "w-12 h-12 rounded-xl flex items-center justify-center mb-4",
248
433
  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" }) })
434
+ children: /* @__PURE__ */ jsx10("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", style: { color: "var(--text-muted)" }, children: /* @__PURE__ */ jsx10("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
435
  }
251
436
  ),
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 }),
437
+ /* @__PURE__ */ jsx10("div", { className: "text-sm font-medium mb-1", style: { color: "var(--text-secondary)" }, children: title }),
438
+ description && /* @__PURE__ */ jsx10("div", { className: "text-xs mb-4", style: { color: "var(--text-muted)" }, children: description }),
254
439
  action
255
440
  ] });
256
441
  }
257
442
 
258
443
  // 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";
444
+ import { useState as useState2, useRef, useEffect as useEffect3 } from "react";
445
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
261
446
  function CustomSelect({
262
447
  options,
263
448
  value,
@@ -266,7 +451,7 @@ function CustomSelect({
266
451
  className = "",
267
452
  size = "md"
268
453
  }) {
269
- const [open, setOpen] = useState(false);
454
+ const [open, setOpen] = useState2(false);
270
455
  const ref = useRef(null);
271
456
  const selected = options.find((o) => o.value === value);
272
457
  const label = selected?.label ?? placeholder ?? "Select...";
@@ -287,8 +472,8 @@ function CustomSelect({
287
472
  return () => document.removeEventListener("keydown", handler);
288
473
  }, [open]);
289
474
  const textSize = size === "sm" ? "text-xs" : "text-sm";
290
- return /* @__PURE__ */ jsxs7("div", { ref, className: `relative ${className}`, children: [
291
- /* @__PURE__ */ jsxs7(
475
+ return /* @__PURE__ */ jsxs10("div", { ref, className: `relative ${className}`, children: [
476
+ /* @__PURE__ */ jsxs10(
292
477
  "button",
293
478
  {
294
479
  type: "button",
@@ -296,8 +481,8 @@ function CustomSelect({
296
481
  className: `admin-input w-full flex items-center justify-between gap-2 ${textSize} text-left`,
297
482
  style: { color: selected ? "var(--text-primary)" : "var(--text-muted)" },
298
483
  children: [
299
- /* @__PURE__ */ jsx8("span", { className: "truncate", children: label }),
300
- /* @__PURE__ */ jsx8(
484
+ /* @__PURE__ */ jsx11("span", { className: "truncate", children: label }),
485
+ /* @__PURE__ */ jsx11(
301
486
  "svg",
302
487
  {
303
488
  width: "12",
@@ -313,13 +498,13 @@ function CustomSelect({
313
498
  color: "var(--text-muted)",
314
499
  transform: open ? "rotate(180deg)" : "rotate(0deg)"
315
500
  },
316
- children: /* @__PURE__ */ jsx8("path", { d: "M3 4.5L6 7.5L9 4.5" })
501
+ children: /* @__PURE__ */ jsx11("path", { d: "M3 4.5L6 7.5L9 4.5" })
317
502
  }
318
503
  )
319
504
  ]
320
505
  }
321
506
  ),
322
- open && /* @__PURE__ */ jsx8(
507
+ open && /* @__PURE__ */ jsx11(
323
508
  "div",
324
509
  {
325
510
  className: "absolute left-0 right-0 top-full mt-1 z-30 py-1 max-h-48 overflow-y-auto",
@@ -329,7 +514,7 @@ function CustomSelect({
329
514
  borderRadius: "var(--radius-md)",
330
515
  boxShadow: "0 8px 24px rgba(0,0,0,0.4)"
331
516
  },
332
- children: options.map((opt) => /* @__PURE__ */ jsx8(
517
+ children: options.map((opt) => /* @__PURE__ */ jsx11(
333
518
  "button",
334
519
  {
335
520
  type: "button",
@@ -358,22 +543,22 @@ function CustomSelect({
358
543
  }
359
544
 
360
545
  // src/components/PageShell.tsx
361
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
546
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
362
547
  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 })
548
+ return /* @__PURE__ */ jsxs11("div", { className: `p-6 lg:p-8 ${maxWidth ?? ""}`, children: [
549
+ /* @__PURE__ */ jsxs11("div", { className: "page-header", children: [
550
+ /* @__PURE__ */ jsxs11("div", { children: [
551
+ /* @__PURE__ */ jsx12("h1", { className: "page-title", children: title }),
552
+ subtitle && /* @__PURE__ */ jsx12("p", { className: "page-subtitle", children: subtitle })
368
553
  ] }),
369
554
  action
370
555
  ] }),
371
- /* @__PURE__ */ jsx9("div", { className: "animate-fade-in", children })
556
+ /* @__PURE__ */ jsx12("div", { className: "animate-fade-in", children })
372
557
  ] });
373
558
  }
374
559
 
375
560
  // src/components/DataTable.tsx
376
- import { useState as useState2, useMemo } from "react";
561
+ import { useState as useState3, useMemo } from "react";
377
562
  import {
378
563
  useReactTable,
379
564
  getCoreRowModel,
@@ -381,7 +566,7 @@ import {
381
566
  getFilteredRowModel,
382
567
  flexRender
383
568
  } from "@tanstack/react-table";
384
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
569
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
385
570
  function DataTable({
386
571
  columns,
387
572
  data,
@@ -391,8 +576,8 @@ function DataTable({
391
576
  enableSearch = true,
392
577
  onRowClick
393
578
  }) {
394
- const [sorting, setSorting] = useState2([]);
395
- const [globalFilter, setGlobalFilter] = useState2("");
579
+ const [sorting, setSorting] = useState3([]);
580
+ const [globalFilter, setGlobalFilter] = useState3("");
396
581
  const stableColumns = useMemo(() => columns, [columns]);
397
582
  const table = useReactTable({
398
583
  data,
@@ -405,10 +590,10 @@ function DataTable({
405
590
  getFilteredRowModel: getFilteredRowModel()
406
591
  });
407
592
  if (isLoading) {
408
- return /* @__PURE__ */ jsx10("div", { className: "py-12 text-center", style: { color: "var(--text-muted)" }, children: "Loading\u2026" });
593
+ return /* @__PURE__ */ jsx13("div", { className: "py-12 text-center", style: { color: "var(--text-muted)" }, children: "Loading\u2026" });
409
594
  }
410
- return /* @__PURE__ */ jsxs9("div", { children: [
411
- enableSearch && /* @__PURE__ */ jsx10("div", { className: "mb-4 max-w-xs", children: /* @__PURE__ */ jsx10(
595
+ return /* @__PURE__ */ jsxs12("div", { children: [
596
+ enableSearch && /* @__PURE__ */ jsx13("div", { className: "mb-4 max-w-xs", children: /* @__PURE__ */ jsx13(
412
597
  SearchInput,
413
598
  {
414
599
  value: globalFilter,
@@ -416,21 +601,21 @@ function DataTable({
416
601
  placeholder: searchPlaceholder ?? "Search..."
417
602
  }
418
603
  ) }),
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(
604
+ table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx13(EmptyState, { title: emptyMessage }) : /* @__PURE__ */ jsx13("div", { className: "admin-card overflow-hidden", children: /* @__PURE__ */ jsxs12("table", { className: "admin-table", children: [
605
+ /* @__PURE__ */ jsx13("thead", { children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsx13("tr", { children: hg.headers.map((header) => /* @__PURE__ */ jsx13(
421
606
  "th",
422
607
  {
423
608
  className: header.column.getCanSort() ? "cursor-pointer select-none" : "",
424
609
  onClick: header.column.getToggleSortingHandler(),
425
- children: /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1", children: [
610
+ children: /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
426
611
  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" })
612
+ header.column.getIsSorted() === "asc" && /* @__PURE__ */ jsx13("span", { style: { color: "var(--accent-text)" }, children: "\u25B2" }),
613
+ header.column.getIsSorted() === "desc" && /* @__PURE__ */ jsx13("span", { style: { color: "var(--accent-text)" }, children: "\u25BC" })
429
614
  ] })
430
615
  },
431
616
  header.id
432
617
  )) }, hg.id)) }),
433
- /* @__PURE__ */ jsx10("tbody", { children: table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx10(
618
+ /* @__PURE__ */ jsx13("tbody", { children: table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx13(
434
619
  "tr",
435
620
  {
436
621
  onClick: () => onRowClick?.(row.original),
@@ -442,7 +627,7 @@ function DataTable({
442
627
  onMouseLeave: (e) => {
443
628
  if (onRowClick) e.currentTarget.style.background = "";
444
629
  },
445
- children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx10("td", { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
630
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx13("td", { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
446
631
  },
447
632
  row.id
448
633
  )) })
@@ -451,7 +636,7 @@ function DataTable({
451
636
  }
452
637
 
453
638
  // src/components/AlertBanner.tsx
454
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
639
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
455
640
  var STYLES = {
456
641
  warning: {
457
642
  bg: "rgba(251,191,36,0.06)",
@@ -468,16 +653,16 @@ var STYLES = {
468
653
  };
469
654
  function AlertBanner({ type, title, children }) {
470
655
  const s = STYLES[type];
471
- return /* @__PURE__ */ jsx11(
656
+ return /* @__PURE__ */ jsx14(
472
657
  "div",
473
658
  {
474
659
  className: "rounded-lg p-3 text-sm",
475
660
  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 })
661
+ children: /* @__PURE__ */ jsxs13("div", { className: "flex items-start gap-2", children: [
662
+ /* @__PURE__ */ jsx14("span", { className: "flex-shrink-0 text-sm leading-5", children: s.icon }),
663
+ /* @__PURE__ */ jsxs13("div", { children: [
664
+ /* @__PURE__ */ jsx14("div", { className: "font-medium text-xs mb-0.5", style: { color: s.titleColor }, children: title }),
665
+ /* @__PURE__ */ jsx14("div", { style: { color: "var(--text-muted)", fontSize: "0.75rem", lineHeight: "1.4" }, children })
481
666
  ] })
482
667
  ] })
483
668
  }
@@ -485,8 +670,8 @@ function AlertBanner({ type, title, children }) {
485
670
  }
486
671
 
487
672
  // 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";
673
+ import { useState as useState4, useCallback } from "react";
674
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
490
675
  function truncateAddress(address) {
491
676
  const prefix = "DIRECT://";
492
677
  if (address.startsWith(prefix)) {
@@ -501,7 +686,7 @@ function truncateAddress(address) {
501
686
  return address;
502
687
  }
503
688
  function AddressDisplay({ address, nametag, truncate = true }) {
504
- const [copied, setCopied] = useState3(false);
689
+ const [copied, setCopied] = useState4(false);
505
690
  const handleCopy = useCallback(() => {
506
691
  navigator.clipboard.writeText(address).then(() => {
507
692
  setCopied(true);
@@ -509,13 +694,13 @@ function AddressDisplay({ address, nametag, truncate = true }) {
509
694
  });
510
695
  }, [address]);
511
696
  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: [
697
+ return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1.5 min-w-0", children: [
698
+ /* @__PURE__ */ jsx15("div", { className: "min-w-0 flex-1", children: nametag ? /* @__PURE__ */ jsxs14(Fragment2, { children: [
699
+ /* @__PURE__ */ jsxs14("div", { className: "text-sm font-medium", style: { color: "var(--text-primary)" }, children: [
515
700
  "@",
516
701
  nametag
517
702
  ] }),
518
- /* @__PURE__ */ jsx12(
703
+ /* @__PURE__ */ jsx15(
519
704
  "div",
520
705
  {
521
706
  className: "text-[11px] font-mono truncate",
@@ -524,7 +709,7 @@ function AddressDisplay({ address, nametag, truncate = true }) {
524
709
  children: displayAddress
525
710
  }
526
711
  )
527
- ] }) : /* @__PURE__ */ jsx12(
712
+ ] }) : /* @__PURE__ */ jsx15(
528
713
  "div",
529
714
  {
530
715
  className: "text-xs font-mono truncate",
@@ -533,7 +718,7 @@ function AddressDisplay({ address, nametag, truncate = true }) {
533
718
  children: displayAddress
534
719
  }
535
720
  ) }),
536
- /* @__PURE__ */ jsx12(
721
+ /* @__PURE__ */ jsx15(
537
722
  "button",
538
723
  {
539
724
  onClick: handleCopy,
@@ -546,9 +731,9 @@ function AddressDisplay({ address, nametag, truncate = true }) {
546
731
  if (!copied) e.currentTarget.style.color = "var(--text-muted)";
547
732
  },
548
733
  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" })
734
+ children: copied ? /* @__PURE__ */ jsx15("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx15("path", { d: "M20 6L9 17l-5-5" }) }) : /* @__PURE__ */ jsxs14("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
735
+ /* @__PURE__ */ jsx15("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
736
+ /* @__PURE__ */ jsx15("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
552
737
  ] })
553
738
  }
554
739
  )
@@ -556,17 +741,17 @@ function AddressDisplay({ address, nametag, truncate = true }) {
556
741
  }
557
742
 
558
743
  // 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";
744
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback2 } from "react";
745
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
561
746
  function JsonPanel({
562
747
  value,
563
748
  onChange,
564
749
  excludeKeys = [],
565
750
  title = "JSON"
566
751
  }) {
567
- const [text, setText] = useState4("");
568
- const [parseError, setParseError] = useState4(null);
569
- const [isEditing, setIsEditing] = useState4(false);
752
+ const [text, setText] = useState5("");
753
+ const [parseError, setParseError] = useState5(null);
754
+ const [isEditing, setIsEditing] = useState5(false);
570
755
  const textareaRef = useRef2(null);
571
756
  useEffect4(() => {
572
757
  if (isEditing) return;
@@ -590,24 +775,24 @@ function JsonPanel({
590
775
  }, [onChange]);
591
776
  const handleFocus = useCallback2(() => setIsEditing(true), []);
592
777
  const handleBlur = useCallback2(() => setIsEditing(false), []);
593
- const [copied, setCopied] = useState4(false);
778
+ const [copied, setCopied] = useState5(false);
594
779
  const handleCopy = useCallback2(async () => {
595
780
  await navigator.clipboard.writeText(text);
596
781
  setCopied(true);
597
782
  setTimeout(() => setCopied(false), 1500);
598
783
  }, [text]);
599
784
  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: [
785
+ return /* @__PURE__ */ jsxs15("div", { className: "flex flex-col h-full", style: { minWidth: 320 }, children: [
786
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between px-3 py-2", style: { borderBottom: "1px solid var(--border)" }, children: [
787
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
788
+ /* @__PURE__ */ jsxs15("span", { className: "text-xs font-mono font-semibold", style: { color: "var(--text-muted)" }, children: [
604
789
  "{ }",
605
790
  " ",
606
791
  title
607
792
  ] }),
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" })
793
+ parseError && /* @__PURE__ */ jsx16("span", { className: "text-[10px] px-1.5 py-0.5 rounded", style: { background: "rgba(239,68,68,0.1)", color: "#f87171" }, children: "Error" })
609
794
  ] }),
610
- /* @__PURE__ */ jsx13(
795
+ /* @__PURE__ */ jsx16(
611
796
  "button",
612
797
  {
613
798
  onClick: handleCopy,
@@ -628,8 +813,8 @@ function JsonPanel({
628
813
  }
629
814
  )
630
815
  ] }),
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(
816
+ /* @__PURE__ */ jsx16("div", { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxs15("div", { className: "absolute inset-0 flex overflow-auto", children: [
817
+ /* @__PURE__ */ jsx16(
633
818
  "div",
634
819
  {
635
820
  className: "shrink-0 text-right pr-2 pt-3 select-none",
@@ -643,10 +828,10 @@ function JsonPanel({
643
828
  background: "var(--bg-surface)",
644
829
  borderRight: "1px solid var(--border)"
645
830
  },
646
- children: Array.from({ length: lineCount }, (_, i) => /* @__PURE__ */ jsx13("div", { children: i + 1 }, i))
831
+ children: Array.from({ length: lineCount }, (_, i) => /* @__PURE__ */ jsx16("div", { children: i + 1 }, i))
647
832
  }
648
833
  ),
649
- /* @__PURE__ */ jsx13(
834
+ /* @__PURE__ */ jsx16(
650
835
  "textarea",
651
836
  {
652
837
  ref: textareaRef,
@@ -668,11 +853,11 @@ function JsonPanel({
668
853
  }
669
854
  )
670
855
  ] }) }),
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 })
856
+ parseError && /* @__PURE__ */ jsx16("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
857
  ] });
673
858
  }
674
859
  function JsonToggleButton({ active, onClick }) {
675
- return /* @__PURE__ */ jsx13(
860
+ return /* @__PURE__ */ jsx16(
676
861
  "button",
677
862
  {
678
863
  onClick,
@@ -703,8 +888,8 @@ function JsonToggleButton({ active, onClick }) {
703
888
  }
704
889
 
705
890
  // src/components/ChainInput.tsx
706
- import { useState as useState5, useId } from "react";
707
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
891
+ import { useState as useState6, useId } from "react";
892
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
708
893
  var TAG_PALETTES = [
709
894
  { bg: "rgba(16, 185, 129, 0.12)", color: "#34d399", border: "rgba(16, 185, 129, 0.2)" },
710
895
  { bg: "rgba(59, 130, 246, 0.12)", color: "#60a5fa", border: "rgba(59, 130, 246, 0.2)" },
@@ -723,8 +908,8 @@ function tagColor(tag) {
723
908
  return TAG_PALETTES[Math.abs(hash) % TAG_PALETTES.length];
724
909
  }
725
910
  function ChainInput({ chains, suggestions, onChange, size = "md" }) {
726
- const [input, setInput] = useState5("");
727
- const [open, setOpen] = useState5(false);
911
+ const [input, setInput] = useState6("");
912
+ const [open, setOpen] = useState6(false);
728
913
  const inputId = useId();
729
914
  const entries = Object.entries(chains);
730
915
  const available = suggestions.filter((s) => !(s in chains) && (!input || s.toLowerCase().includes(input.toLowerCase())));
@@ -758,8 +943,8 @@ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
758
943
  const isSm = size === "sm";
759
944
  const textSize = isSm ? "text-[0.6rem]" : "text-[0.65rem]";
760
945
  const minH = isSm ? "min-h-[32px]" : "min-h-[38px]";
761
- return /* @__PURE__ */ jsxs13("div", { className: "relative", children: [
762
- /* @__PURE__ */ jsxs13(
946
+ return /* @__PURE__ */ jsxs16("div", { className: "relative", children: [
947
+ /* @__PURE__ */ jsxs16(
763
948
  "div",
764
949
  {
765
950
  className: `admin-input flex flex-wrap gap-1.5 !p-1.5 ${minH} cursor-text`,
@@ -767,14 +952,14 @@ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
767
952
  children: [
768
953
  entries.map(([name, order]) => {
769
954
  const c = tagColor(name);
770
- return /* @__PURE__ */ jsxs13(
955
+ return /* @__PURE__ */ jsxs16(
771
956
  "span",
772
957
  {
773
958
  className: `inline-flex items-center gap-1 ${textSize} font-semibold px-2 py-0.5 rounded`,
774
959
  style: { background: c.bg, color: c.color, border: `1px solid ${c.border}` },
775
960
  children: [
776
961
  name,
777
- /* @__PURE__ */ jsx14(
962
+ /* @__PURE__ */ jsx17(
778
963
  "input",
779
964
  {
780
965
  type: "number",
@@ -786,7 +971,7 @@ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
786
971
  onClick: (e) => e.stopPropagation()
787
972
  }
788
973
  ),
789
- /* @__PURE__ */ jsx14(
974
+ /* @__PURE__ */ jsx17(
790
975
  "button",
791
976
  {
792
977
  type: "button",
@@ -803,7 +988,7 @@ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
803
988
  name
804
989
  );
805
990
  }),
806
- /* @__PURE__ */ jsx14(
991
+ /* @__PURE__ */ jsx17(
807
992
  "input",
808
993
  {
809
994
  id: inputId,
@@ -823,13 +1008,13 @@ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
823
1008
  ]
824
1009
  }
825
1010
  ),
826
- open && (available.length > 0 || isNew) && /* @__PURE__ */ jsxs13(
1011
+ open && (available.length > 0 || isNew) && /* @__PURE__ */ jsxs16(
827
1012
  "div",
828
1013
  {
829
1014
  className: "absolute left-0 right-0 top-full mt-1 z-20 py-1 max-h-40 overflow-y-auto",
830
1015
  style: { background: "var(--bg-elevated)", border: "1px solid var(--border)", borderRadius: "var(--radius-md)" },
831
1016
  children: [
832
- isNew && /* @__PURE__ */ jsxs13(
1017
+ isNew && /* @__PURE__ */ jsxs16(
833
1018
  "button",
834
1019
  {
835
1020
  onMouseDown: () => addChain(trimmed),
@@ -850,7 +1035,7 @@ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
850
1035
  ),
851
1036
  available.map((s) => {
852
1037
  const c = tagColor(s);
853
- return /* @__PURE__ */ jsxs13(
1038
+ return /* @__PURE__ */ jsxs16(
854
1039
  "button",
855
1040
  {
856
1041
  onMouseDown: () => addChain(s),
@@ -863,7 +1048,7 @@ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
863
1048
  e.currentTarget.style.background = "transparent";
864
1049
  },
865
1050
  children: [
866
- /* @__PURE__ */ jsx14("span", { className: "w-2 h-2 rounded-full flex-shrink-0", style: { background: c.color } }),
1051
+ /* @__PURE__ */ jsx17("span", { className: "w-2 h-2 rounded-full flex-shrink-0", style: { background: c.color } }),
867
1052
  s
868
1053
  ]
869
1054
  },
@@ -877,7 +1062,7 @@ function ChainInput({ chains, suggestions, onChange, size = "md" }) {
877
1062
  }
878
1063
 
879
1064
  // src/components/MemoConditionsEditor.tsx
880
- import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1065
+ import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
881
1066
  var MEMO_OPERATORS = [
882
1067
  { value: "eq", label: "Equals (=)", values: 1 },
883
1068
  { value: "neq", label: "Not equals (\u2260)", values: 1 },
@@ -923,16 +1108,16 @@ function MemoConditionsEditor({ conditions, onChange }) {
923
1108
  const remove = (i) => onChange(conditions.filter((_, idx) => idx !== i));
924
1109
  const update = (i, patch) => onChange(conditions.map((c, idx) => idx === i ? { ...c, ...patch } : c));
925
1110
  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" })
1111
+ return /* @__PURE__ */ jsxs17("div", { children: [
1112
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2 mb-2", children: [
1113
+ /* @__PURE__ */ jsx18("span", { className: "text-xs font-medium", style: { color: "var(--text-muted)" }, children: "Memo Conditions" }),
1114
+ /* @__PURE__ */ jsx18("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
1115
  ] }),
931
- conditions.length > 0 ? /* @__PURE__ */ jsx15("div", { className: "flex flex-col gap-2", children: conditions.map((cond, i) => {
1116
+ conditions.length > 0 ? /* @__PURE__ */ jsx18("div", { className: "flex flex-col gap-2", children: conditions.map((cond, i) => {
932
1117
  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(
1118
+ return /* @__PURE__ */ jsxs17("div", { className: "rounded-lg p-2.5", style: { background: "rgba(255,255,255,0.02)", border: "1px solid var(--border)" }, children: [
1119
+ /* @__PURE__ */ jsxs17("div", { className: "flex gap-2 items-center", children: [
1120
+ /* @__PURE__ */ jsx18(
936
1121
  "input",
937
1122
  {
938
1123
  className: "admin-input flex-1 text-xs",
@@ -941,7 +1126,7 @@ function MemoConditionsEditor({ conditions, onChange }) {
941
1126
  onChange: (e) => update(i, { key: e.target.value })
942
1127
  }
943
1128
  ),
944
- /* @__PURE__ */ jsx15(
1129
+ /* @__PURE__ */ jsx18(
945
1130
  CustomSelect,
946
1131
  {
947
1132
  size: "sm",
@@ -951,7 +1136,7 @@ function MemoConditionsEditor({ conditions, onChange }) {
951
1136
  options: MEMO_OPERATORS.map((o) => ({ value: o.value, label: o.label }))
952
1137
  }
953
1138
  ),
954
- meta.values >= 1 && /* @__PURE__ */ jsx15(
1139
+ meta.values >= 1 && /* @__PURE__ */ jsx18(
955
1140
  "input",
956
1141
  {
957
1142
  className: "admin-input flex-1 text-xs",
@@ -960,9 +1145,9 @@ function MemoConditionsEditor({ conditions, onChange }) {
960
1145
  onChange: (e) => update(i, { value: e.target.value })
961
1146
  }
962
1147
  ),
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(
1148
+ meta.values === 2 && /* @__PURE__ */ jsxs17(Fragment3, { children: [
1149
+ /* @__PURE__ */ jsx18("span", { className: "text-[10px]", style: { color: "var(--text-muted)" }, children: "and" }),
1150
+ /* @__PURE__ */ jsx18(
966
1151
  "input",
967
1152
  {
968
1153
  className: "admin-input flex-1 text-xs",
@@ -972,18 +1157,18 @@ function MemoConditionsEditor({ conditions, onChange }) {
972
1157
  }
973
1158
  )
974
1159
  ] }),
975
- /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => remove(i), className: "text-red-400 text-sm px-1 cursor-pointer", children: "\xD7" })
1160
+ /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => remove(i), className: "text-red-400 text-sm px-1 cursor-pointer", children: "\xD7" })
976
1161
  ] }),
977
- /* @__PURE__ */ jsx15("p", { className: "text-[10px] mt-1.5 ml-0.5", style: { color: "var(--text-muted)", fontStyle: "italic" }, children: conditionPreview(cond) })
1162
+ /* @__PURE__ */ jsx18("p", { className: "text-[10px] mt-1.5 ml-0.5", style: { color: "var(--text-muted)", fontStyle: "italic" }, children: conditionPreview(cond) })
978
1163
  ] }, 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.' })
1164
+ }) }) : /* @__PURE__ */ jsx18("p", { className: "text-[10px]", style: { color: "var(--text-muted)" }, children: 'No conditions \u2014 matches any memo transaction. Click "+ Add" to filter.' })
980
1165
  ] });
981
1166
  }
982
1167
 
983
1168
  // src/components/Icons.tsx
984
- import { jsx as jsx16 } from "react/jsx-runtime";
1169
+ import { jsx as jsx19 } from "react/jsx-runtime";
985
1170
  function I({ d, size = 16, className, style }) {
986
- return /* @__PURE__ */ jsx16(
1171
+ return /* @__PURE__ */ jsx19(
987
1172
  "svg",
988
1173
  {
989
1174
  width: size,
@@ -996,7 +1181,7 @@ function I({ d, size = 16, className, style }) {
996
1181
  strokeLinejoin: "round",
997
1182
  className,
998
1183
  style,
999
- children: /* @__PURE__ */ jsx16("path", { d })
1184
+ children: /* @__PURE__ */ jsx19("path", { d })
1000
1185
  }
1001
1186
  );
1002
1187
  }
@@ -1028,34 +1213,36 @@ var P = {
1028
1213
  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
1214
  circle: "M12 12m-4 0a4 4 0 1 0 8 0 4 4 0 1 0-8 0"
1030
1215
  };
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 });
1216
+ var IconBack = (p) => /* @__PURE__ */ jsx19(I, { d: P.back, ...p });
1217
+ var IconUndo = (p) => /* @__PURE__ */ jsx19(I, { d: P.undo, ...p });
1218
+ var IconQuests = (p) => /* @__PURE__ */ jsx19(I, { d: P.quests, ...p });
1219
+ var IconTracks = (p) => /* @__PURE__ */ jsx19(I, { d: P.tracks, ...p });
1220
+ var IconSettings = (p) => /* @__PURE__ */ jsx19(I, { d: P.settings, ...p });
1221
+ var IconChain = (p) => /* @__PURE__ */ jsx19(I, { d: P.chain, ...p });
1222
+ var IconPlus = (p) => /* @__PURE__ */ jsx19(I, { d: P.plus, ...p });
1223
+ var IconEdit = (p) => /* @__PURE__ */ jsx19(I, { d: P.edit, ...p });
1224
+ var IconTrash = (p) => /* @__PURE__ */ jsx19(I, { d: P.trash, ...p });
1225
+ var IconX = (p) => /* @__PURE__ */ jsx19(I, { d: P.x, ...p });
1226
+ var IconCheck = (p) => /* @__PURE__ */ jsx19(I, { d: P.check, ...p });
1227
+ var IconSearch = (p) => /* @__PURE__ */ jsx19(I, { d: P.search, ...p });
1228
+ var IconChevronUp = (p) => /* @__PURE__ */ jsx19(I, { d: P.chevronUp, ...p });
1229
+ var IconChevronDown = (p) => /* @__PURE__ */ jsx19(I, { d: P.chevronDown, ...p });
1230
+ var IconChevronsDown = (p) => /* @__PURE__ */ jsx19(I, { d: P.chevronsDown, ...p });
1231
+ var IconChevronsRight = (p) => /* @__PURE__ */ jsx19(I, { d: P.chevronsRight, ...p });
1232
+ var IconArrowRight = (p) => /* @__PURE__ */ jsx19(I, { d: P.arrowRight, ...p });
1233
+ var IconPlay = (p) => /* @__PURE__ */ jsx19(I, { d: P.play, ...p });
1234
+ var IconStar = (p) => /* @__PURE__ */ jsx19(I, { d: P.star, ...p });
1235
+ var IconDiamond = (p) => /* @__PURE__ */ jsx19(I, { d: P.diamond, ...p });
1236
+ var IconCircle = (p) => /* @__PURE__ */ jsx19(I, { d: P.circle, ...p });
1052
1237
  export {
1053
1238
  AddressDisplay,
1054
1239
  AlertBanner,
1240
+ AppLogo,
1055
1241
  Button,
1056
1242
  ChainInput,
1057
1243
  ConfirmDialog,
1058
1244
  CustomSelect,
1245
+ DashboardLayout,
1059
1246
  DataTable,
1060
1247
  EmptyState,
1061
1248
  Field,
@@ -1089,6 +1276,7 @@ export {
1089
1276
  SearchInput,
1090
1277
  Section,
1091
1278
  Select,
1279
+ SidebarNav,
1092
1280
  StatusBadge,
1093
1281
  Textarea,
1094
1282
  tagColor
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unicitylabs/sphere-ui",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -76,6 +76,17 @@ body {
76
76
  ::-webkit-scrollbar-thumb { background: #1a1a1a; border-radius: 10px; }
77
77
  ::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.15); }
78
78
 
79
- /* Anton (--font-display) is NOT applied globally to headings.
80
- Sphere uses Geist for all text by default.
81
- Apply Anton explicitly where needed: style={{ fontFamily: 'var(--font-display)' }} */
79
+ /* Anton for headings in dashboard apps (admin, dev portal).
80
+ Add class="sphere-dashboard" on <body> or root to activate.
81
+ Sphere wallet does NOT use this it uses Geist everywhere. */
82
+ .sphere-dashboard h1,
83
+ .sphere-dashboard h2,
84
+ .sphere-dashboard h3 {
85
+ font-family: var(--font-display);
86
+ font-weight: 400;
87
+ letter-spacing: 0.02em;
88
+ text-transform: uppercase;
89
+ -webkit-font-smoothing: antialiased;
90
+ -moz-osx-font-smoothing: grayscale;
91
+ text-rendering: geometricPrecision;
92
+ }