@espressif/dashboard-ui-components 1.5.0 → 1.7.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.
@@ -110,6 +110,37 @@ Things agents get wrong about it:
110
110
  full page reload.
111
111
  - No `prose` wrapper or spacing utilities are needed; it owns its own vertical rhythm.
112
112
 
113
+ ## Editing code and JSON
114
+
115
+ `CodeEditor` (from `/components`) is the editable counterpart of `Code`: a form field
116
+ with syntax highlighting. Use it instead of `Textarea` for JSON payloads, policies or
117
+ templates; use `Code` for read-only display.
118
+
119
+ ```tsx
120
+ import { CodeEditor } from '@espressif/dashboard-ui-components/components'
121
+
122
+ <CodeEditor
123
+ {...field} // react-hook-form: native onChange + ref to the <textarea>
124
+ lang="json" // "text" for base64 / opaque data
125
+ theme={dark ? 'dark' : 'light'}
126
+ label="Payload"
127
+ errorContent={fieldState.error?.message}
128
+ onFormatError={(e) => setFormatError(String(e))}
129
+ />
130
+ ```
131
+
132
+ Things agents get wrong about it:
133
+
134
+ - `onChange` receives the native change event, like `Textarea` — there is no `onValueChange`.
135
+ - It does **not** validate. The built-in Format button (JSON only) pretty-prints and
136
+ reports invalid JSON to `onFormatError`; schema rules (zod etc.) stay in the app.
137
+ Pass `formatter` for other languages; do not reimplement Format in `headerActions`.
138
+ - Esc, then Tab, moves focus out (Tab otherwise indents). Inside a `Sheet` or `Dialog`,
139
+ Esc also reaches the overlay's own Escape handler and closes it; guard with
140
+ `onEscapeKeyDown={(e) => { if ((e.target as Element).closest('[data-slot="code-editor"]')) e.preventDefault() }}`
141
+ if that matters.
142
+ - `className` / `style` style the editor box, not the `<textarea>`.
143
+
113
144
  ## Bundle size and tooling (what agents should assume)
114
145
 
115
146
  - The published library is **pre-bundled ESM** (tsup). Consumers receive **large `dist` chunks**, not one file per source component.
@@ -107,6 +107,37 @@ Things agents get wrong about it:
107
107
  full page reload.
108
108
  - No `prose` wrapper or spacing utilities are needed; it owns its own vertical rhythm.
109
109
 
110
+ ## Editing code and JSON
111
+
112
+ `CodeEditor` (from `/components`) is the editable counterpart of `Code`: a form field
113
+ with syntax highlighting. Use it instead of `Textarea` for JSON payloads, policies or
114
+ templates; use `Code` for read-only display.
115
+
116
+ ```tsx
117
+ import { CodeEditor } from '@espressif/dashboard-ui-components/components'
118
+
119
+ <CodeEditor
120
+ {...field} // react-hook-form: native onChange + ref to the <textarea>
121
+ lang="json" // "text" for base64 / opaque data
122
+ theme={dark ? 'dark' : 'light'}
123
+ label="Payload"
124
+ errorContent={fieldState.error?.message}
125
+ onFormatError={(e) => setFormatError(String(e))}
126
+ />
127
+ ```
128
+
129
+ Things agents get wrong about it:
130
+
131
+ - `onChange` receives the native change event, like `Textarea` — there is no `onValueChange`.
132
+ - It does **not** validate. The built-in Format button (JSON only) pretty-prints and
133
+ reports invalid JSON to `onFormatError`; schema rules (zod etc.) stay in the app.
134
+ Pass `formatter` for other languages; do not reimplement Format in `headerActions`.
135
+ - Esc, then Tab, moves focus out (Tab otherwise indents). Inside a `Sheet` or `Dialog`,
136
+ Esc also reaches the overlay's own Escape handler and closes it; guard with
137
+ `onEscapeKeyDown={(e) => { if ((e.target as Element).closest('[data-slot="code-editor"]')) e.preventDefault() }}`
138
+ if that matters.
139
+ - `className` / `style` style the editor box, not the `<textarea>`.
140
+
110
141
  ## Bundle size and tooling (what agents should assume)
111
142
 
112
143
  - The published library is **pre-bundled ESM** (tsup). Consumers receive **large `dist` chunks**, not one file per source component.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,173 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres
5
5
  to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.7.0] - 2026-10-06
8
+
9
+ A minor release adding per-item icons and colours to `OverflowBadgeList`
10
+ and element icons to `StatusBadge`, with fixes to the collapsed `Sidebar`
11
+ flyout menus and the `WorkspaceLayout` account menu. No breaking changes.
12
+
13
+ ### Added
14
+ - **`OverflowBadgeList`** — per-item icons and colours. `items` now also
15
+ accepts `{ id?, label, icon?, color? }` objects (exported as
16
+ `OverflowBadgeListItem`), which can be mixed with plain strings. Each
17
+ object item renders its own `icon` and uses its own `color` in place of
18
+ `badgeColor`, so a list can show, for example, a brand icon per
19
+ integration and grey out inactive entries. Plain-string items behave as
20
+ before: the list-level `icon` appears on the first badge only.
21
+ - **`StatusBadge`** — `icon` also accepts a pre-built element (such as a
22
+ custom SVG brand icon) in addition to a Lucide component. Elements are
23
+ rendered as-is, so the caller sets their size. The type is exported as
24
+ `StatusBadgeIcon`. No breaking changes.
25
+
26
+ ### Changed
27
+ - **`IconTextActionCard`, `SimpleClickableCard`, `SimpleInfoCard`** — a
28
+ `title` passed as a ReactNode now gets the same title typography (size and
29
+ weight for the card `size`) as a string title, instead of inheriting the
30
+ surrounding text style. It is still wrapped in a `div`, so block content
31
+ stays valid. Elements inside the title that set their own font size or
32
+ weight are unaffected; plain text inside it becomes the title weight.
33
+
34
+ ### Fixed
35
+ - **`StatusBadge` / `OverflowBadgeList`** — badges with an icon no longer
36
+ sit higher than badges without one.
37
+ - **`Sidebar` / `WorkspaceLayout`** — collapsed sidebar flyout menus:
38
+ - Choosing an item no longer brings back the group's label tooltip.
39
+ Closing the flyout with Escape or the keyboard still returns focus to
40
+ the group icon.
41
+ - Clicking anywhere on a row now navigates and closes the flyout.
42
+ Previously, with a router `linkComponent`, clicking the empty part of a
43
+ row navigated but left the flyout open, and clicking the row's edge
44
+ closed it without navigating.
45
+ - Choosing an item with Enter or Space now navigates to it. Previously the
46
+ flyout closed without navigating.
47
+ - **`WorkspaceLayout`** — clicking the profile card in the account menu
48
+ (when `onProfileCardClick` is set) now closes the menu after the callback
49
+ runs. The card also highlights on hover like the other menu rows and can
50
+ be reached with the arrow keys and chosen with Enter or Space.
51
+
52
+ ### Internal
53
+ Nothing in this section affects the published package.
54
+
55
+ - Demo: the landing page's right panel now plays a looping, silent showreel
56
+ of the library's components, rendered from the real components by
57
+ `demo/video` (`npm run video:beats` / `npm run video:render`).
58
+ - Demo: the `OverflowBadgeList` page gains a per-item icons example, and the
59
+ `WorkspaceLayout` page wires `onProfileCardClick` / `onLogoutClick` and
60
+ documents `onProfileCardClick`.
61
+ - Tests for `IconTextActionCard`, `OverflowBadgeList`, `Sidebar` and the
62
+ `WorkspaceLayout` account menu.
63
+
64
+ ## [1.6.0] - 2026-09-25
65
+
66
+ A minor release adding an editable code field (`CodeEditor`), a warning
67
+ state to `StatusCardList`, fixed-value fields to `KeyValueManager`, and
68
+ rolling presets plus a clear action to `DateRangePicker`. Focus rings now
69
+ follow the primary colour, and a render loop in `DataTable` is fixed. No
70
+ breaking changes.
71
+
72
+ ### Added
73
+ - **`CodeEditor`** — an editable, syntax-highlighted code field. It has the
74
+ same header, file name, copy button and Shiki themes as `Code`, and works
75
+ in a form like `Textarea`: controlled or uncontrolled, native `onChange` /
76
+ `onBlur` / `name`, a ref to the `<textarea>`, so `{...field}` and
77
+ `{...register(name)}` from react-hook-form work unchanged (including
78
+ values written by `setValue` / `reset`). Carries `label`, `required`,
79
+ `hintContent`, `error`, `errorContent`, `disabled`, `readOnly` and
80
+ `placeholder`.
81
+ - Tab indents (every line of a multi-line selection), Shift+Tab outdents,
82
+ Enter keeps the current indentation, and all three are undoable with
83
+ Cmd/Ctrl+Z. Press Esc, then Tab, to move focus out of the editor. Keys
84
+ pressed during IME composition are left alone.
85
+ - For `lang="json"` the header has a Format button that pretty-prints with
86
+ `tabSize` spaces (undoable, fires `onChange`). Invalid JSON is left
87
+ unchanged and passed to `onFormatError`. `formatter` supplies a
88
+ formatter for any language; `formatButton={false}` hides the button;
89
+ `formatLabel` sets its accessible name, and `copyLabel` sets the copy
90
+ button's. `headerActions` adds further
91
+ header controls.
92
+ - `hideHeader` removes the header. It is also left out automatically when
93
+ there is no `fileName`, no `headerActions`, and neither a copy nor a
94
+ format button.
95
+ - Other props: `lang` (default `"json"`; `"text"` for base64 or other
96
+ opaque data), `theme` / `themes`, `fileName`, `icon`, `copyButton`,
97
+ `minHeight` / `maxHeight` of the editing area, `wrap` (soft-wrap long
98
+ lines; off by default) and `tabSize` (default 2).
99
+ - Above `maxHighlightLength` characters (default 10 000) the editor shows
100
+ plain text, so typing in a large document stays responsive. Highlighting
101
+ one keystroke of a 64 KB JSON document takes about 64 ms on an Apple M4,
102
+ and a 10 KB one about 13 ms. Languages or themes outside the curated
103
+ Shiki bundle also render as plain text, with one console warning per
104
+ language.
105
+ - Text renders at 16px below the `md` breakpoint so iOS Safari does not
106
+ zoom on focus, and at 13px (like `Code`) from `md` up.
107
+ - **`DateRangePicker`** — rolling presets and a clear action:
108
+ - New presets `last-1-hour`, `last-24-hours`, `past-7-days` and
109
+ `past-30-days`. They end at the moment they are selected instead of
110
+ snapping to day boundaries, so `past-7-days` and `past-30-days` include
111
+ today (the existing `last-7-days` and `last-30-days` end yesterday at
112
+ 23:59 and are unchanged). `ROLLING_PRESET_IDS` lists them, so a consumer
113
+ can recompute the range with `computeDateRange` when it refreshes data.
114
+ - New `clearable` prop. While a preset or custom range is selected, the
115
+ menu shows a "Clear" item that calls the new `onClear` callback and
116
+ closes the menu. `clearLabel` overrides its text (default `"Clear"`).
117
+ - Code with an exhaustive `switch` over `DateRangePickerPresetId` must
118
+ handle the four new IDs.
119
+ - **`KeyValueManager`** — two new `KeyValueManagerFieldMeta` options:
120
+ - `options` renders the field in the add popover as a select limited to
121
+ those values, and rejects any other value. Use it when the allowed values
122
+ are known up front, for example the header columns of an uploaded CSV.
123
+ - `regexFlags` is passed to the `RegExp` built from `regex`, so a pattern
124
+ can use Unicode property escapes such as `\p{L}` with `"u"`. Patterns
125
+ without flags behave as before.
126
+ - New label `selectPlaceholder` (default `"Select"`) for fields with
127
+ `options`.
128
+ - **`StatusCardList`** — new `"warning"` value for `StatusCardState`, for a
129
+ step that finished but only partly succeeded (for example, a bulk job where
130
+ some items failed). It uses the `warning` colour token for the card, avatar
131
+ and text, and a `TriangleAlert` end icon. Code with an exhaustive `switch`
132
+ over `StatusCardState` must handle the new value.
133
+ - **AI agent rules** — `.claude/rules/dashboard-ui-consumption.md` and
134
+ `.cursor/rules/dashboard-ui-consumption.mdc` gain an "Editing code and JSON"
135
+ section on `CodeEditor`: when to use it instead of `Textarea` / `Code`, the
136
+ react-hook-form wiring, and the usual mistakes (there is no `onValueChange`,
137
+ it does not validate, Esc inside a `Sheet` / `Dialog`).
138
+
139
+ ### Changed
140
+ - **Focus ring colour** — `--color-ring` now defaults to `var(--color-primary)`
141
+ in light and dark mode instead of fixed blues (`#0053c2` / `#7cd9ff`).
142
+ Components that draw their focus ring with `ring-ring` (`Input`, `Select`,
143
+ `Textarea`, `SearchBox`, `AdvancedSearchBox`, `ToggleGroup`, `Tabs`,
144
+ `Switch`, `Checkbox`, `Dialog` and others) previously kept the blue ring in
145
+ apps that re-theme `primary`, while `Button` and the clickable cards already
146
+ used `ring-primary`, so focus rings in the same toolbar did not match.
147
+ - With the default theme, light mode's ring moves from `#0053c2` to the
148
+ primary `#0071e3`; dark mode is unchanged (both were `#7cd9ff`).
149
+ - Apps that want a ring distinct from primary can still set `--color-ring`.
150
+ - **`DateRangePicker`** — a picker without a `presets` prop shows every
151
+ preset, so its menu now also lists the four new rolling presets. They sit
152
+ among the trailing ranges, which run shortest to longest: "Last 1 hour" and
153
+ "Last 24 hours" come before "Last 1 day", and "Past 7 days" / "Past 30 days"
154
+ come just before "Last 7 days" / "Last 30 days". `PRESET_IDS` follows the
155
+ same order. Pass `presets` to keep the previous menu.
156
+ - **`Code`** — the header's copy button now has an accessible name and
157
+ tooltip (`aria-label` / `title` "Copy"); before, the icon-only button had
158
+ no name for screen readers. The header is now shared with `CodeEditor` and
159
+ is otherwise unchanged.
160
+
161
+ ### Fixed
162
+ - **`DataTable`** — no longer triggers an extra internal re-render every time
163
+ `data` changes identity. TanStack Table resets the page index when `data`
164
+ changes unless pagination is manual, and each reset was a table-internal
165
+ state update. The page index here is always 0 (paging belongs to the
166
+ consumer through `pageIndex` / `onNextPage` / `onPrevPage`), so the reset
167
+ did nothing useful. With a consumer passing a fresh array on every render
168
+ (`data?.items ?? []`), those updates could pile up into React's "Maximum
169
+ update depth exceeded" under load, for example when switching to a
170
+ server-paginated table while the main thread was busy. The table now sets
171
+ `autoResetPageIndex: false` and memoizes its pagination state. Rows are
172
+ still capped at `pageSize`, so existing tables render the same.
173
+
7
174
  ## [1.5.0] - 2026-09-22
8
175
 
9
176
  A minor release adding a combined date and time control.
package/README.md CHANGED
@@ -455,6 +455,7 @@ version you have installed is the shipped type declarations:
455
455
  | Alert | Alert banners (optionally dismissible) |
456
456
  | Link | Styled anchor element — colour-tinted pill, or `variant="ghost"` for a bare inline link |
457
457
  | Code | Syntax-highlighted code blocks |
458
+ | CodeEditor | Editable, syntax-highlighted code field (`Code` look, `Textarea` form behaviour), with Format (JSON) and Copy buttons |
458
459
  | MarkdownContent | Renders a markdown string with library primitives (GFM, heading anchors; raw HTML is escaped, not parsed) |
459
460
  | CopiableText / CopyButton | Copy-to-clipboard utilities |
460
461
  | ProgressBar | Progress indicator |
@@ -3637,11 +3637,16 @@ function ItemIcon({
3637
3637
  if (active) return /* @__PURE__ */ jsx(CircleDot, { className });
3638
3638
  return /* @__PURE__ */ jsx(Circle, { className: cn("opacity-50", className) });
3639
3639
  }
3640
+ function forwardRowClickToLink(e) {
3641
+ if (e.target !== e.currentTarget) return;
3642
+ e.currentTarget.firstElementChild?.click();
3643
+ }
3640
3644
  function GroupMenuItems({
3641
3645
  group,
3642
3646
  isSelected,
3643
3647
  linkComponent: LinkComponent,
3644
- onItemClick
3648
+ onItemClick,
3649
+ onItemPointerDown
3645
3650
  }) {
3646
3651
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3647
3652
  /* @__PURE__ */ jsxs(
@@ -3662,10 +3667,21 @@ function GroupMenuItems({
3662
3667
  /* @__PURE__ */ jsx(DropdownMenuSeparator2, {}),
3663
3668
  group.items.map((item) => {
3664
3669
  const active = isSelected(item.path);
3665
- return /* @__PURE__ */ jsx(DropdownMenuItem2, { className: active ? "bg-accent" : "", style: rowFontStyle, children: /* @__PURE__ */ jsx(LinkComponent, { to: item.path, className: "flex items-center gap-2 cursor-pointer w-full", children: /* @__PURE__ */ jsxs("span", { className: "contents", onClick: onItemClick, children: [
3666
- /* @__PURE__ */ jsx(ItemIcon, { icon: item.icon, active }),
3667
- /* @__PURE__ */ jsx("span", { className: active ? "font-semibold" : "font-medium", children: item.label })
3668
- ] }) }) }, item.id);
3670
+ return /* @__PURE__ */ jsx(
3671
+ DropdownMenuItem2,
3672
+ {
3673
+ className: active ? "bg-accent" : "",
3674
+ style: rowFontStyle,
3675
+ onPointerDown: onItemPointerDown,
3676
+ onClickCapture: onItemClick,
3677
+ onClick: forwardRowClickToLink,
3678
+ children: /* @__PURE__ */ jsxs(LinkComponent, { to: item.path, className: "flex items-center gap-2 cursor-pointer w-full", children: [
3679
+ /* @__PURE__ */ jsx(ItemIcon, { icon: item.icon, active }),
3680
+ /* @__PURE__ */ jsx("span", { className: active ? "font-semibold" : "font-medium", children: item.label })
3681
+ ] })
3682
+ },
3683
+ item.id
3684
+ );
3669
3685
  })
3670
3686
  ] });
3671
3687
  }
@@ -3675,28 +3691,45 @@ function CollapsedSidebarGroup({
3675
3691
  linkComponent: LinkComponent
3676
3692
  }) {
3677
3693
  const [open, setOpen] = useState(false);
3678
- return /* @__PURE__ */ jsx(SidebarGroup, { className: sidebarGroupClassName, children: /* @__PURE__ */ jsx(SidebarMenu, { className: "w-auto", children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(DropdownMenu2, { open, onOpenChange: setOpen, children: [
3679
- /* @__PURE__ */ jsx(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx(SidebarMenuButton, { size: "sm", tooltip: group.label, className: rowIconClass, children: /* @__PURE__ */ jsx(ItemIcon, { icon: group.icon }) }) }),
3680
- /* @__PURE__ */ jsx(
3681
- DropdownMenuContent2,
3682
- {
3683
- side: "right",
3684
- align: "start",
3685
- sideOffset: 8,
3686
- className: "z-50 max-w-xs rounded-2xl shadow-lg will-change-[opacity,transform] py-1.5 overflow-visible select-none",
3687
- style: { minWidth: 180 },
3688
- children: /* @__PURE__ */ jsx(
3689
- GroupMenuItems,
3694
+ const pickedWithPointer = useRef(false);
3695
+ return /* @__PURE__ */ jsx(SidebarGroup, { className: sidebarGroupClassName, children: /* @__PURE__ */ jsx(SidebarMenu, { className: "w-auto", children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(
3696
+ DropdownMenu2,
3697
+ {
3698
+ open,
3699
+ onOpenChange: (next) => {
3700
+ if (next) pickedWithPointer.current = false;
3701
+ setOpen(next);
3702
+ },
3703
+ children: [
3704
+ /* @__PURE__ */ jsx(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx(SidebarMenuButton, { size: "sm", tooltip: group.label, className: rowIconClass, children: /* @__PURE__ */ jsx(ItemIcon, { icon: group.icon }) }) }),
3705
+ /* @__PURE__ */ jsx(
3706
+ DropdownMenuContent2,
3690
3707
  {
3691
- group,
3692
- isSelected,
3693
- linkComponent: LinkComponent,
3694
- onItemClick: () => setOpen(false)
3708
+ side: "right",
3709
+ align: "start",
3710
+ sideOffset: 8,
3711
+ className: "z-50 max-w-xs rounded-2xl shadow-lg will-change-[opacity,transform] py-1.5 overflow-visible select-none",
3712
+ style: { minWidth: 180 },
3713
+ onCloseAutoFocus: (e) => {
3714
+ if (pickedWithPointer.current) e.preventDefault();
3715
+ },
3716
+ children: /* @__PURE__ */ jsx(
3717
+ GroupMenuItems,
3718
+ {
3719
+ group,
3720
+ isSelected,
3721
+ linkComponent: LinkComponent,
3722
+ onItemClick: () => setOpen(false),
3723
+ onItemPointerDown: () => {
3724
+ pickedWithPointer.current = true;
3725
+ }
3726
+ }
3727
+ )
3695
3728
  }
3696
3729
  )
3697
- }
3698
- )
3699
- ] }) }) }) });
3730
+ ]
3731
+ }
3732
+ ) }) }) });
3700
3733
  }
3701
3734
  var groupHeaderClass = cn(
3702
3735
  // No `flex-1`: this button is a column flex item, so growing made it take its
@@ -1,5 +1,5 @@
1
- import { FooterCard, MyAccountMenu, ProfileCard, AppLogo } from './chunk-45DCL46L.js';
2
- import { useSidebar, SidebarTrigger, SidebarProvider, Sidebar2, SidebarInset, parseTextMarkup, isPathPrefixOf, isSidebarGroup } from './chunk-ZEQNTNMW.js';
1
+ import { FooterCard, MyAccountMenu, ProfileCard, AppLogo } from './chunk-UA4DS3KA.js';
2
+ import { useSidebar, SidebarTrigger, DropdownMenuItem, SidebarProvider, Sidebar2, SidebarInset, parseTextMarkup, isPathPrefixOf, isSidebarGroup } from './chunk-3PO2SF7T.js';
3
3
  import { Button, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator } from './chunk-OKB2WTX7.js';
4
4
  import { cn } from './chunk-VU4CZ76T.js';
5
5
  import { Menu, Palette, Check, Globe, LogOut } from 'lucide-react';
@@ -88,15 +88,19 @@ function AccountMenu({
88
88
  groups.push({
89
89
  id: "profile",
90
90
  items: [
91
- onProfileCardClick ? /* @__PURE__ */ jsx(
92
- "button",
93
- {
94
- type: "button",
95
- onClick: onProfileCardClick,
96
- className: "w-full text-left cursor-pointer",
97
- children: profileCard
98
- },
99
- "profile"
91
+ onProfileCardClick ? (
92
+ // A menu item (not a plain button) so Radix closes the menu on select and
93
+ // includes the card in keyboard navigation. Negative margins cancel the
94
+ // custom-item wrapper's padding so the card keeps its position; they sit on
95
+ // an outer div because the hover highlight is sized from the item's wrapper.
96
+ /* @__PURE__ */ jsx("div", { className: "-mx-2.5 -my-1.5", children: /* @__PURE__ */ jsx(
97
+ DropdownMenuItem,
98
+ {
99
+ onSelect: onProfileCardClick,
100
+ className: "px-2.5 py-1.5 cursor-pointer *:flex-1 *:min-w-0",
101
+ children: profileCard
102
+ }
103
+ ) }, "profile")
100
104
  ) : profileCard
101
105
  ]
102
106
  });
@@ -1,4 +1,4 @@
1
- import { Avatar, Menu, List, Button, Link } from './chunk-ZEQNTNMW.js';
1
+ import { Avatar, Menu, List, Button, Link } from './chunk-3PO2SF7T.js';
2
2
  import { cn } from './chunk-VU4CZ76T.js';
3
3
  import { motion, AnimatePresence } from 'framer-motion';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';