@medialane/ui 0.61.0 → 0.63.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.
@@ -14,12 +14,106 @@ function useNavCommandMenu() {
14
14
  close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE))
15
15
  };
16
16
  }
17
+ const RECENT_KEY = "ml.nav.recent";
18
+ const RECENT_MAX = 3;
19
+ function readRecents() {
20
+ try {
21
+ const raw = localStorage.getItem(RECENT_KEY);
22
+ const ids = raw ? JSON.parse(raw) : [];
23
+ return Array.isArray(ids) ? ids.filter((x) => typeof x === "string") : [];
24
+ } catch {
25
+ return [];
26
+ }
27
+ }
28
+ function pushRecent(id) {
29
+ try {
30
+ const next = [id, ...readRecents().filter((x) => x !== id)].slice(0, RECENT_MAX);
31
+ localStorage.setItem(RECENT_KEY, JSON.stringify(next));
32
+ } catch {
33
+ }
34
+ }
35
+ function Kbd({ children, className }) {
36
+ return /* @__PURE__ */ jsx(
37
+ "kbd",
38
+ {
39
+ className: cn(
40
+ "inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5",
41
+ "font-sans text-[10px] leading-none text-muted-foreground",
42
+ className
43
+ ),
44
+ children
45
+ }
46
+ );
47
+ }
48
+ function CommandRow({ item, primary, onSelect }) {
49
+ return /* @__PURE__ */ jsxs(
50
+ Command.Item,
51
+ {
52
+ value: [item.label, ...item.keywords ?? []].join(" "),
53
+ onSelect,
54
+ className: cn(
55
+ "group/item flex cursor-pointer items-center gap-3 rounded-xl",
56
+ "transition-colors duration-150 aria-selected:bg-primary/10",
57
+ primary ? "px-2.5 py-2.5" : "px-2.5 py-2"
58
+ ),
59
+ children: [
60
+ /* @__PURE__ */ jsx(
61
+ "span",
62
+ {
63
+ className: cn(
64
+ "flex shrink-0 items-center justify-center rounded-lg transition-colors",
65
+ "bg-muted/50 group-aria-selected/item:bg-primary/15",
66
+ primary ? "h-9 w-9" : "h-8 w-8"
67
+ ),
68
+ children: /* @__PURE__ */ jsx(
69
+ item.icon,
70
+ {
71
+ className: cn(
72
+ "text-foreground/80 transition-colors group-aria-selected/item:text-primary",
73
+ primary ? "h-[18px] w-[18px]" : "h-4 w-4"
74
+ )
75
+ }
76
+ )
77
+ }
78
+ ),
79
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
80
+ /* @__PURE__ */ jsx(
81
+ "span",
82
+ {
83
+ className: cn(
84
+ "block truncate leading-tight",
85
+ primary ? "text-[15px] font-medium" : "text-sm"
86
+ ),
87
+ children: item.label
88
+ }
89
+ ),
90
+ item.description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 block truncate text-[11.5px] leading-tight text-muted-foreground", children: item.description })
91
+ ] }),
92
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground/30 transition-colors group-aria-selected/item:text-primary/70" })
93
+ ]
94
+ }
95
+ );
96
+ }
97
+ const GROUP_HEADING_CLASSES = cn(
98
+ "[&_[cmdk-group-heading]]:px-2.5",
99
+ "[&_[cmdk-group-heading]]:pt-2",
100
+ "[&_[cmdk-group-heading]]:pb-1",
101
+ "[&_[cmdk-group-heading]]:text-[10.5px]",
102
+ "[&_[cmdk-group-heading]]:font-semibold",
103
+ "[&_[cmdk-group-heading]]:uppercase",
104
+ "[&_[cmdk-group-heading]]:tracking-[0.08em]",
105
+ "[&_[cmdk-group-heading]]:text-muted-foreground/60"
106
+ );
17
107
  function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
18
108
  const [open, setOpen] = React.useState(false);
109
+ const [query, setQuery] = React.useState("");
110
+ const [recentIds, setRecentIds] = React.useState([]);
19
111
  const router = useRouter();
20
112
  const inputRef = React.useRef(null);
21
113
  React.useEffect(() => {
22
114
  if (!open) return;
115
+ setQuery("");
116
+ setRecentIds(readRecents());
23
117
  const t = setTimeout(() => inputRef.current?.focus(), 60);
24
118
  return () => clearTimeout(t);
25
119
  }, [open]);
@@ -44,12 +138,19 @@ function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
44
138
  }, []);
45
139
  const runCommand = React.useCallback(
46
140
  (cmd) => {
141
+ pushRecent(cmd.id);
47
142
  setOpen(false);
48
143
  if (cmd.href) router.push(cmd.href);
49
144
  else cmd.action?.();
50
145
  },
51
146
  [router]
52
147
  );
148
+ const recentItems = React.useMemo(() => {
149
+ if (recentIds.length === 0) return [];
150
+ const all = new Map(commands.flatMap((g) => g.items.map((it) => [it.id, it])));
151
+ return recentIds.map((id) => all.get(id)).filter((it) => Boolean(it));
152
+ }, [recentIds, commands]);
153
+ const showRecents = query === "" && recentItems.length > 0;
53
154
  return /* @__PURE__ */ jsxs(Fragment, { children: [
54
155
  trigger,
55
156
  /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -67,86 +168,100 @@ function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
67
168
  /* @__PURE__ */ jsx(
68
169
  motion.div,
69
170
  {
70
- className: "fixed inset-0 z-[101] flex items-center justify-center p-4",
71
- initial: { opacity: 0, scale: 0.97 },
72
- animate: { opacity: 1, scale: 1 },
73
- exit: { opacity: 0, scale: 0.97 },
74
- transition: { duration: 0.15, ease: "easeOut" },
171
+ className: "fixed inset-0 z-[101] flex items-end justify-center p-3 pb-4 sm:items-center sm:p-4",
172
+ initial: { opacity: 0, y: 24, scale: 1 },
173
+ animate: { opacity: 1, y: 0, scale: 1 },
174
+ exit: { opacity: 0, y: 24 },
175
+ transition: { duration: 0.18, ease: "easeOut" },
75
176
  onClick: () => setOpen(false),
76
177
  children: /* @__PURE__ */ jsx(
77
178
  "div",
78
179
  {
79
- className: "w-full max-w-lg bg-background/90 border border-border/40 rounded-2xl overflow-hidden shadow-2xl",
180
+ className: cn(
181
+ "flex w-full max-h-[85dvh] flex-col overflow-hidden rounded-[20px] sm:max-h-[min(680px,88dvh)] sm:max-w-[620px]",
182
+ "border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150"
183
+ ),
80
184
  onClick: (e) => e.stopPropagation(),
81
- children: /* @__PURE__ */ jsxs(Command, { shouldFilter: true, label: "Medialane navigation", children: [
82
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3.5 border-b border-border/40", children: [
83
- /* @__PURE__ */ jsx(Search, { className: "h-[18px] w-[18px] text-muted-foreground shrink-0" }),
185
+ children: /* @__PURE__ */ jsxs(Command, { shouldFilter: true, label: "Medialane navigation", className: "flex min-h-0 flex-1 flex-col", children: [
186
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center pt-2.5 sm:hidden", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "h-1 w-9 rounded-full bg-muted-foreground/30" }) }),
187
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 border-b border-border/40 px-4 py-3.5", children: [
188
+ /* @__PURE__ */ jsx(
189
+ Search,
190
+ {
191
+ className: cn(
192
+ "h-[18px] w-[18px] shrink-0 transition-colors",
193
+ query ? "text-primary" : "text-muted-foreground"
194
+ )
195
+ }
196
+ ),
84
197
  /* @__PURE__ */ jsx(
85
198
  Command.Input,
86
199
  {
87
200
  ref: inputRef,
88
- placeholder: "Type a command or search\u2026",
201
+ value: query,
202
+ onValueChange: setQuery,
203
+ placeholder: "Search or run a command\u2026",
89
204
  className: "flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground"
90
205
  }
91
206
  ),
207
+ /* @__PURE__ */ jsx(Kbd, { className: "hidden sm:inline-flex", children: "esc" }),
92
208
  /* @__PURE__ */ jsx(
93
209
  "button",
94
210
  {
95
211
  onClick: () => setOpen(false),
96
- className: "p-1 rounded-md hover:bg-muted/50 transition-colors",
212
+ className: "rounded-md p-1 transition-colors hover:bg-muted/50 sm:hidden",
97
213
  "aria-label": "Close",
98
214
  children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4 text-muted-foreground" })
99
215
  }
100
216
  )
101
217
  ] }),
102
- /* @__PURE__ */ jsxs(Command.List, { className: "max-h-[60vh] overflow-y-auto p-2", children: [
218
+ /* @__PURE__ */ jsxs(Command.List, { className: "min-h-0 flex-1 overflow-y-auto overscroll-contain p-2", children: [
103
219
  /* @__PURE__ */ jsx(Command.Empty, { className: "py-8 text-center text-sm text-muted-foreground", children: "No results found." }),
220
+ showRecents && /* @__PURE__ */ jsx(Command.Group, { heading: "Recent", className: GROUP_HEADING_CLASSES, children: recentItems.map((item) => /* @__PURE__ */ jsx(
221
+ CommandRow,
222
+ {
223
+ item,
224
+ primary: false,
225
+ onSelect: () => runCommand(item)
226
+ },
227
+ `recent-${item.id}`
228
+ )) }),
104
229
  commands.map((group, i) => {
105
230
  const primary = !group.heading;
106
231
  return /* @__PURE__ */ jsxs(React.Fragment, { children: [
107
- i > 0 && /* @__PURE__ */ jsx(Command.Separator, { className: "my-1.5 h-px bg-border/40" }),
108
- /* @__PURE__ */ jsx(
109
- Command.Group,
232
+ (i > 0 || showRecents) && /* @__PURE__ */ jsx(Command.Separator, { className: "my-1.5 h-px bg-border/40" }),
233
+ /* @__PURE__ */ jsx(Command.Group, { heading: group.heading, className: GROUP_HEADING_CLASSES, children: group.items.map((item) => /* @__PURE__ */ jsx(
234
+ CommandRow,
110
235
  {
111
- heading: group.heading,
112
- className: cn(
113
- "[&_[cmdk-group-heading]]:px-2",
114
- "[&_[cmdk-group-heading]]:pt-1.5",
115
- "[&_[cmdk-group-heading]]:pb-1",
116
- "[&_[cmdk-group-heading]]:text-[11px]",
117
- "[&_[cmdk-group-heading]]:font-semibold",
118
- "[&_[cmdk-group-heading]]:uppercase",
119
- "[&_[cmdk-group-heading]]:tracking-wider",
120
- "[&_[cmdk-group-heading]]:text-muted-foreground/70"
121
- ),
122
- children: group.items.map((item) => /* @__PURE__ */ jsxs(
123
- Command.Item,
124
- {
125
- value: [item.label, ...item.keywords ?? []].join(" "),
126
- onSelect: () => runCommand(item),
127
- className: cn(
128
- "group/item flex items-center gap-3 rounded-xl cursor-pointer",
129
- "transition-colors duration-150",
130
- "aria-selected:bg-primary/10",
131
- primary ? "px-2.5 py-2.5 text-[15px] font-medium" : "px-3 py-2 text-sm"
132
- ),
133
- children: [
134
- primary ? /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-muted/50 group-aria-selected/item:bg-primary/15 transition-colors shrink-0", children: /* @__PURE__ */ jsx(item.icon, { className: "h-[18px] w-[18px] text-foreground/80 group-aria-selected/item:text-primary transition-colors" }) }) : /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 text-muted-foreground group-aria-selected/item:text-foreground transition-colors shrink-0" }),
135
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: item.label }),
136
- /* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5 text-muted-foreground/30 group-aria-selected/item:text-primary/70 transition-colors shrink-0" })
137
- ]
138
- },
139
- item.id
140
- ))
141
- }
142
- )
236
+ item,
237
+ primary,
238
+ onSelect: () => runCommand(item)
239
+ },
240
+ item.id
241
+ )) })
143
242
  ] }, group.heading ?? `__primary-${i}`);
144
243
  })
145
244
  ] }),
146
- accountSlot && /* @__PURE__ */ jsx("div", { className: "border-t border-border/40 bg-background/40 px-3 py-3", children: accountSlot }),
147
- /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 border-t border-border/40 flex items-center justify-between gap-3", children: [
148
- footerSlot ?? /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/50 pl-1", children: "medialane" }),
149
- /* @__PURE__ */ jsx("kbd", { className: "text-[10px] text-muted-foreground/50 font-mono shrink-0", children: "\u2318K" })
245
+ accountSlot && /* @__PURE__ */ jsx("div", { className: "border-t border-border/40 bg-muted/20 px-3 py-3", children: accountSlot }),
246
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2", children: [
247
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
248
+ footerSlot,
249
+ /* @__PURE__ */ jsxs("span", { className: "hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex", children: [
250
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
251
+ /* @__PURE__ */ jsx(Kbd, { children: "\u2191" }),
252
+ /* @__PURE__ */ jsx(Kbd, { children: "\u2193" }),
253
+ " Navigate"
254
+ ] }),
255
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
256
+ /* @__PURE__ */ jsx(Kbd, { children: "\u21B5" }),
257
+ " Open"
258
+ ] })
259
+ ] })
260
+ ] }),
261
+ /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50", children: [
262
+ "medialane",
263
+ /* @__PURE__ */ jsx(Kbd, { className: "hidden sm:inline-flex", children: "\u2318K" })
264
+ ] })
150
265
  ] })
151
266
  ] })
152
267
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-center justify-center p-4\"\n initial={{ opacity: 0, scale: 0.97 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.97 }}\n transition={{ duration: 0.15, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className=\"w-full max-w-lg bg-background/90 border border-border/40 rounded-2xl overflow-hidden shadow-2xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\">\n {/* Search bar */}\n <div className=\"flex items-center gap-3 px-4 py-3.5 border-b border-border/40\">\n <Search className=\"h-[18px] w-[18px] text-muted-foreground shrink-0\" />\n <Command.Input\n ref={inputRef}\n placeholder=\"Type a command or search…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <button\n onClick={() => setOpen(false)}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"max-h-[60vh] overflow-y-auto p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group\n heading={group.heading}\n className={cn(\n \"[&_[cmdk-group-heading]]:px-2\",\n \"[&_[cmdk-group-heading]]:pt-1.5\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[11px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-wider\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/70\"\n )}\n >\n {group.items.map((item) => (\n <Command.Item\n key={item.id}\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={() => runCommand(item)}\n className={cn(\n \"group/item flex items-center gap-3 rounded-xl cursor-pointer\",\n \"transition-colors duration-150\",\n \"aria-selected:bg-primary/10\",\n primary\n ? \"px-2.5 py-2.5 text-[15px] font-medium\"\n : \"px-3 py-2 text-sm\"\n )}\n >\n {primary ? (\n <span className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-muted/50 group-aria-selected/item:bg-primary/15 transition-colors shrink-0\">\n <item.icon className=\"h-[18px] w-[18px] text-foreground/80 group-aria-selected/item:text-primary transition-colors\" />\n </span>\n ) : (\n <item.icon className=\"h-4 w-4 text-muted-foreground group-aria-selected/item:text-foreground transition-colors shrink-0\" />\n )}\n <span className=\"flex-1 truncate\">{item.label}</span>\n <ArrowRight className=\"h-3.5 w-3.5 text-muted-foreground/30 group-aria-selected/item:text-primary/70 transition-colors shrink-0\" />\n </Command.Item>\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-background/40 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"px-3 py-2 border-t border-border/40 flex items-center justify-between gap-3\">\n {footerSlot ?? (\n <span className=\"text-[10px] text-muted-foreground/50 pl-1\">medialane</span>\n )}\n <kbd className=\"text-[10px] text-muted-foreground/50 font-mono shrink-0\">⌘K</kbd>\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n /** Optional one-line description shown under the label */\n description?: string;\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Recents (localStorage) ─────────────────────────────────────────────────────\n\nconst RECENT_KEY = \"ml.nav.recent\";\nconst RECENT_MAX = 3;\n\nfunction readRecents(): string[] {\n try {\n const raw = localStorage.getItem(RECENT_KEY);\n const ids = raw ? (JSON.parse(raw) as unknown) : [];\n return Array.isArray(ids) ? ids.filter((x): x is string => typeof x === \"string\") : [];\n } catch {\n return [];\n }\n}\n\nfunction pushRecent(id: string) {\n try {\n const next = [id, ...readRecents().filter((x) => x !== id)].slice(0, RECENT_MAX);\n localStorage.setItem(RECENT_KEY, JSON.stringify(next));\n } catch {\n /* storage unavailable — recents are a nicety, never an error */\n }\n}\n\n// ── Small primitives ──────────────────────────────────────────────────────────\n\nfunction Kbd({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <kbd\n className={cn(\n \"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5\",\n \"font-sans text-[10px] leading-none text-muted-foreground\",\n className\n )}\n >\n {children}\n </kbd>\n );\n}\n\nfunction CommandRow({ item, primary, onSelect }: { item: NavCommand; primary: boolean; onSelect: () => void }) {\n return (\n <Command.Item\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={onSelect}\n className={cn(\n \"group/item flex cursor-pointer items-center gap-3 rounded-xl\",\n \"transition-colors duration-150 aria-selected:bg-primary/10\",\n primary ? \"px-2.5 py-2.5\" : \"px-2.5 py-2\"\n )}\n >\n <span\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg transition-colors\",\n \"bg-muted/50 group-aria-selected/item:bg-primary/15\",\n primary ? \"h-9 w-9\" : \"h-8 w-8\"\n )}\n >\n <item.icon\n className={cn(\n \"text-foreground/80 transition-colors group-aria-selected/item:text-primary\",\n primary ? \"h-[18px] w-[18px]\" : \"h-4 w-4\"\n )}\n />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n \"block truncate leading-tight\",\n primary ? \"text-[15px] font-medium\" : \"text-sm\"\n )}\n >\n {item.label}\n </span>\n {item.description && (\n <span className=\"mt-0.5 block truncate text-[11.5px] leading-tight text-muted-foreground\">\n {item.description}\n </span>\n )}\n </span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/30 transition-colors group-aria-selected/item:text-primary/70\" />\n </Command.Item>\n );\n}\n\nconst GROUP_HEADING_CLASSES = cn(\n \"[&_[cmdk-group-heading]]:px-2.5\",\n \"[&_[cmdk-group-heading]]:pt-2\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[10.5px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-[0.08em]\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/60\"\n);\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const [query, setQuery] = React.useState(\"\");\n const [recentIds, setRecentIds] = React.useState<string[]>([]);\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n setQuery(\"\");\n setRecentIds(readRecents());\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n pushRecent(cmd.id);\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n // Recents only render while the query is empty (search results replace them).\n const recentItems = React.useMemo(() => {\n if (recentIds.length === 0) return [];\n const all = new Map(commands.flatMap((g) => g.items.map((it) => [it.id, it] as const)));\n return recentIds.map((id) => all.get(id)).filter((it): it is NavCommand => Boolean(it));\n }, [recentIds, commands]);\n const showRecents = query === \"\" && recentItems.length > 0;\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel — centered on desktop, bottom sheet on mobile */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-end justify-center p-3 pb-4 sm:items-center sm:p-4\"\n initial={{ opacity: 0, y: 24, scale: 1 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 24 }}\n transition={{ duration: 0.18, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className={cn(\n \"flex w-full max-h-[85dvh] flex-col overflow-hidden rounded-[20px] sm:max-h-[min(680px,88dvh)] sm:max-w-[620px]\",\n \"border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150\"\n )}\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\" className=\"flex min-h-0 flex-1 flex-col\">\n {/* Drag handle (mobile) */}\n <div className=\"flex justify-center pt-2.5 sm:hidden\" aria-hidden=\"true\">\n <span className=\"h-1 w-9 rounded-full bg-muted-foreground/30\" />\n </div>\n\n {/* Search bar */}\n <div className=\"flex items-center gap-3 border-b border-border/40 px-4 py-3.5\">\n <Search\n className={cn(\n \"h-[18px] w-[18px] shrink-0 transition-colors\",\n query ? \"text-primary\" : \"text-muted-foreground\"\n )}\n />\n <Command.Input\n ref={inputRef}\n value={query}\n onValueChange={setQuery}\n placeholder=\"Search or run a command…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <Kbd className=\"hidden sm:inline-flex\">esc</Kbd>\n <button\n onClick={() => setOpen(false)}\n className=\"rounded-md p-1 transition-colors hover:bg-muted/50 sm:hidden\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {showRecents && (\n <Command.Group heading=\"Recent\" className={GROUP_HEADING_CLASSES}>\n {recentItems.map((item) => (\n <CommandRow\n key={`recent-${item.id}`}\n item={item}\n primary={false}\n onSelect={() => runCommand(item)}\n />\n ))}\n </Command.Group>\n )}\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {(i > 0 || showRecents) && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group heading={group.heading} className={GROUP_HEADING_CLASSES}>\n {group.items.map((item) => (\n <CommandRow\n key={item.id}\n item={item}\n primary={primary}\n onSelect={() => runCommand(item)}\n />\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-muted/20 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2\">\n <div className=\"flex items-center gap-3\">\n {footerSlot}\n <span className=\"hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex\">\n <span className=\"flex items-center gap-1\"><Kbd>↑</Kbd><Kbd>↓</Kbd> Navigate</span>\n <span className=\"flex items-center gap-1\"><Kbd>↵</Kbd> Open</span>\n </span>\n </div>\n <span className=\"flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50\">\n medialane\n <Kbd className=\"hidden sm:inline-flex\">⌘K</Kbd>\n </span>\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var nav_shell_exports = {};
31
+ __export(nav_shell_exports, {
32
+ NavAccountSheet: () => NavAccountSheet,
33
+ NavBrandButton: () => NavBrandButton,
34
+ NavIconButton: () => NavIconButton,
35
+ useNavAccountSheet: () => useNavAccountSheet
36
+ });
37
+ module.exports = __toCommonJS(nav_shell_exports);
38
+ var import_jsx_runtime = require("react/jsx-runtime");
39
+ var React = __toESM(require("react"), 1);
40
+ var import_framer_motion = require("framer-motion");
41
+ var import_cn = require("../utils/cn.js");
42
+ function NavBrandButton({ onClick, className, iconSrc = "/icon.png", ...rest }) {
43
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
44
+ "button",
45
+ {
46
+ type: "button",
47
+ onClick,
48
+ "aria-label": rest["aria-label"] ?? "Open navigation",
49
+ className: (0, import_cn.cn)(
50
+ "group flex h-11 items-center gap-2 rounded-full border border-border/40",
51
+ "bg-background/70 pl-1.5 pr-3 backdrop-blur-xl backdrop-saturate-150 shadow-sm",
52
+ "transition-colors hover:bg-background/90 active:scale-[0.97]",
53
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
54
+ className
55
+ ),
56
+ children: [
57
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: iconSrc, alt: "Medialane", width: 30, height: 30, className: "h-[30px] w-[30px] shrink-0" }),
58
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex flex-col gap-[5px]", "aria-hidden": "true", children: [
59
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "h-[1.5px] w-4 rounded-full bg-muted-foreground transition-all group-hover:w-4 group-hover:bg-foreground" }),
60
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "h-[1.5px] w-2.5 rounded-full bg-muted-foreground transition-all group-hover:w-4 group-hover:bg-foreground" })
61
+ ] })
62
+ ]
63
+ }
64
+ );
65
+ }
66
+ function NavIconButton({ onClick, className, indicator, children, ...rest }) {
67
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
68
+ "button",
69
+ {
70
+ type: "button",
71
+ onClick,
72
+ "aria-label": rest["aria-label"],
73
+ className: (0, import_cn.cn)(
74
+ "relative flex h-11 w-11 items-center justify-center rounded-full border border-border/40",
75
+ "bg-background/70 text-muted-foreground backdrop-blur-xl backdrop-saturate-150 shadow-sm",
76
+ "transition-colors hover:bg-background/90 hover:text-foreground active:scale-[0.97]",
77
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
78
+ className
79
+ ),
80
+ children: [
81
+ children,
82
+ indicator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "absolute right-[3px] top-[3px] h-2.5 w-2.5 rounded-full border-2 border-background bg-brand-blue" })
83
+ ]
84
+ }
85
+ );
86
+ }
87
+ const ML_ACCOUNT_OPEN = "ml:account-open";
88
+ const ML_ACCOUNT_CLOSE = "ml:account-close";
89
+ const ML_NAV_OPEN = "ml:nav-open";
90
+ const ML_NAV_CLOSE = "ml:nav-close";
91
+ function useNavAccountSheet() {
92
+ return {
93
+ open: () => document.dispatchEvent(new CustomEvent(ML_ACCOUNT_OPEN)),
94
+ close: () => document.dispatchEvent(new CustomEvent(ML_ACCOUNT_CLOSE))
95
+ };
96
+ }
97
+ function NavAccountSheet({ children, title = "Account" }) {
98
+ const [open, setOpen] = React.useState(false);
99
+ React.useEffect(() => {
100
+ const onOpen = () => {
101
+ document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE));
102
+ setOpen(true);
103
+ };
104
+ const onClose = () => setOpen(false);
105
+ const onKey = (e) => {
106
+ if (e.key === "Escape") setOpen(false);
107
+ };
108
+ document.addEventListener(ML_ACCOUNT_OPEN, onOpen);
109
+ document.addEventListener(ML_ACCOUNT_CLOSE, onClose);
110
+ document.addEventListener(ML_NAV_OPEN, onClose);
111
+ document.addEventListener(ML_NAV_CLOSE, onClose);
112
+ document.addEventListener("keydown", onKey);
113
+ return () => {
114
+ document.removeEventListener(ML_ACCOUNT_OPEN, onOpen);
115
+ document.removeEventListener(ML_ACCOUNT_CLOSE, onClose);
116
+ document.removeEventListener(ML_NAV_OPEN, onClose);
117
+ document.removeEventListener(ML_NAV_CLOSE, onClose);
118
+ document.removeEventListener("keydown", onKey);
119
+ };
120
+ }, []);
121
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_framer_motion.AnimatePresence, { children: open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
123
+ import_framer_motion.motion.div,
124
+ {
125
+ className: "nav-canvas-overlay",
126
+ initial: { opacity: 0 },
127
+ animate: { opacity: 1 },
128
+ exit: { opacity: 0 },
129
+ transition: { duration: 0.15 },
130
+ onClick: () => setOpen(false)
131
+ }
132
+ ),
133
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
134
+ import_framer_motion.motion.div,
135
+ {
136
+ className: "fixed inset-x-3 bottom-3 z-[101] sm:inset-auto sm:right-4 sm:top-4 sm:w-[360px] lg:right-8",
137
+ initial: { opacity: 0, y: 24 },
138
+ animate: { opacity: 1, y: 0 },
139
+ exit: { opacity: 0, y: 24 },
140
+ transition: { duration: 0.18, ease: "easeOut" },
141
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "overflow-hidden rounded-[20px] border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150", children: [
142
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex justify-center pt-2.5 sm:hidden", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "h-1 w-9 rounded-full bg-muted-foreground/30" }) }),
143
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between px-4 pb-1 pt-2 sm:pt-3", children: [
144
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70", children: title }),
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
146
+ "button",
147
+ {
148
+ onClick: () => setOpen(false),
149
+ className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted/50",
150
+ "aria-label": "Close",
151
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) })
152
+ }
153
+ )
154
+ ] }),
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-3 pb-4 pt-1 sm:pb-3", children })
156
+ ] })
157
+ }
158
+ )
159
+ ] }) });
160
+ }
161
+ // Annotate the CommonJS export names for ESM import in node:
162
+ 0 && (module.exports = {
163
+ NavAccountSheet,
164
+ NavBrandButton,
165
+ NavIconButton,
166
+ useNavAccountSheet
167
+ });
168
+ //# sourceMappingURL=nav-shell.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/nav-shell.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Header buttons ────────────────────────────────────────────────────────────\n\nexport interface NavBrandButtonProps {\n onClick?: () => void;\n className?: string;\n \"aria-label\"?: string;\n /** Path to the brand icon image (the app's real icon asset). */\n iconSrc?: string;\n}\n\n/**\n * The main header trigger: brand mark + menu glyph in one quiet glass pill.\n * Mobile-first — a single ≥44px tap target instead of two small adjacent icons.\n */\nexport function NavBrandButton({ onClick, className, iconSrc = \"/icon.png\", ...rest }: NavBrandButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={rest[\"aria-label\"] ?? \"Open navigation\"}\n className={cn(\n \"group flex h-11 items-center gap-2 rounded-full border border-border/40\",\n \"bg-background/70 pl-1.5 pr-3 backdrop-blur-xl backdrop-saturate-150 shadow-sm\",\n \"transition-colors hover:bg-background/90 active:scale-[0.97]\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60\",\n className\n )}\n >\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img src={iconSrc} alt=\"Medialane\" width={30} height={30} className=\"h-[30px] w-[30px] shrink-0\" />\n <span className=\"flex flex-col gap-[5px]\" aria-hidden=\"true\">\n <span className=\"h-[1.5px] w-4 rounded-full bg-muted-foreground transition-all group-hover:w-4 group-hover:bg-foreground\" />\n <span className=\"h-[1.5px] w-2.5 rounded-full bg-muted-foreground transition-all group-hover:w-4 group-hover:bg-foreground\" />\n </span>\n </button>\n );\n}\n\nexport interface NavIconButtonProps {\n onClick?: () => void;\n className?: string;\n \"aria-label\": string;\n /** Show a small brand-colored status dot (e.g. wallet connected). */\n indicator?: boolean;\n children: React.ReactNode;\n}\n\n/**\n * Circular glass header button — the right-side counterpart to NavBrandButton\n * (wallet / account trigger).\n */\nexport function NavIconButton({ onClick, className, indicator, children, ...rest }: NavIconButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={rest[\"aria-label\"]}\n className={cn(\n \"relative flex h-11 w-11 items-center justify-center rounded-full border border-border/40\",\n \"bg-background/70 text-muted-foreground backdrop-blur-xl backdrop-saturate-150 shadow-sm\",\n \"transition-colors hover:bg-background/90 hover:text-foreground active:scale-[0.97]\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60\",\n className\n )}\n >\n {children}\n {indicator && (\n <span className=\"absolute right-[3px] top-[3px] h-2.5 w-2.5 rounded-full border-2 border-background bg-brand-blue\" />\n )}\n </button>\n );\n}\n\n// ── Account sheet ─────────────────────────────────────────────────────────────\n\nconst ML_ACCOUNT_OPEN = \"ml:account-open\";\nconst ML_ACCOUNT_CLOSE = \"ml:account-close\";\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavAccountSheet() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_ACCOUNT_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_ACCOUNT_CLOSE)),\n };\n}\n\nexport interface NavAccountSheetProps {\n /** The app's account/connect panel (Clerk, wallet connectors, …). */\n children: React.ReactNode;\n /** Optional heading above the panel. */\n title?: string;\n}\n\n/**\n * The wallet-side surface: a compact glass sheet anchored to the top-right\n * header button (bottom sheet on mobile). Content is app-owned — pass the same\n * account panel the command menu uses in its accountSlot.\n *\n * Opens via `useNavAccountSheet().open()`. Mutually exclusive with the command\n * menu: opening one closes the other, and the shared `ml:nav-close` event\n * (fired by `useNavCommandMenu().close()`) closes this sheet too, so account\n * panels behave identically in both hosts.\n */\nexport function NavAccountSheet({ children, title = \"Account\" }: NavAccountSheetProps) {\n const [open, setOpen] = React.useState(false);\n\n React.useEffect(() => {\n const onOpen = () => {\n document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE));\n setOpen(true);\n };\n const onClose = () => setOpen(false);\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(ML_ACCOUNT_OPEN, onOpen);\n document.addEventListener(ML_ACCOUNT_CLOSE, onClose);\n document.addEventListener(ML_NAV_OPEN, onClose);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n document.addEventListener(\"keydown\", onKey);\n return () => {\n document.removeEventListener(ML_ACCOUNT_OPEN, onOpen);\n document.removeEventListener(ML_ACCOUNT_CLOSE, onClose);\n document.removeEventListener(ML_NAV_OPEN, onClose);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n document.removeEventListener(\"keydown\", onKey);\n };\n }, []);\n\n return (\n <AnimatePresence>\n {open && (\n <>\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n <motion.div\n className=\"fixed inset-x-3 bottom-3 z-[101] sm:inset-auto sm:right-4 sm:top-4 sm:w-[360px] lg:right-8\"\n initial={{ opacity: 0, y: 24 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 24 }}\n transition={{ duration: 0.18, ease: \"easeOut\" }}\n >\n <div className=\"overflow-hidden rounded-[20px] border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150\">\n <div className=\"flex justify-center pt-2.5 sm:hidden\" aria-hidden=\"true\">\n <span className=\"h-1 w-9 rounded-full bg-muted-foreground/30\" />\n </div>\n <div className=\"flex items-center justify-between px-4 pb-1 pt-2 sm:pt-3\">\n <span className=\"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70\">\n {title}\n </span>\n <button\n onClick={() => setOpen(false)}\n className=\"rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted/50\"\n aria-label=\"Close\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n </div>\n <div className=\"px-3 pb-4 pt-1 sm:pb-3\">{children}</div>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,51 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ interface NavBrandButtonProps {
5
+ onClick?: () => void;
6
+ className?: string;
7
+ "aria-label"?: string;
8
+ /** Path to the brand icon image (the app's real icon asset). */
9
+ iconSrc?: string;
10
+ }
11
+ /**
12
+ * The main header trigger: brand mark + menu glyph in one quiet glass pill.
13
+ * Mobile-first — a single ≥44px tap target instead of two small adjacent icons.
14
+ */
15
+ declare function NavBrandButton({ onClick, className, iconSrc, ...rest }: NavBrandButtonProps): react_jsx_runtime.JSX.Element;
16
+ interface NavIconButtonProps {
17
+ onClick?: () => void;
18
+ className?: string;
19
+ "aria-label": string;
20
+ /** Show a small brand-colored status dot (e.g. wallet connected). */
21
+ indicator?: boolean;
22
+ children: React.ReactNode;
23
+ }
24
+ /**
25
+ * Circular glass header button — the right-side counterpart to NavBrandButton
26
+ * (wallet / account trigger).
27
+ */
28
+ declare function NavIconButton({ onClick, className, indicator, children, ...rest }: NavIconButtonProps): react_jsx_runtime.JSX.Element;
29
+ declare function useNavAccountSheet(): {
30
+ open: () => boolean;
31
+ close: () => boolean;
32
+ };
33
+ interface NavAccountSheetProps {
34
+ /** The app's account/connect panel (Clerk, wallet connectors, …). */
35
+ children: React.ReactNode;
36
+ /** Optional heading above the panel. */
37
+ title?: string;
38
+ }
39
+ /**
40
+ * The wallet-side surface: a compact glass sheet anchored to the top-right
41
+ * header button (bottom sheet on mobile). Content is app-owned — pass the same
42
+ * account panel the command menu uses in its accountSlot.
43
+ *
44
+ * Opens via `useNavAccountSheet().open()`. Mutually exclusive with the command
45
+ * menu: opening one closes the other, and the shared `ml:nav-close` event
46
+ * (fired by `useNavCommandMenu().close()`) closes this sheet too, so account
47
+ * panels behave identically in both hosts.
48
+ */
49
+ declare function NavAccountSheet({ children, title }: NavAccountSheetProps): react_jsx_runtime.JSX.Element;
50
+
51
+ export { NavAccountSheet, type NavAccountSheetProps, NavBrandButton, type NavBrandButtonProps, NavIconButton, type NavIconButtonProps, useNavAccountSheet };