hadadrjt commited on
Commit
d2843fe
·
1 Parent(s): 7d4d56f

ai: Refac B/F. #3

Browse files
components/assistant-ui/markdown-text.tsx CHANGED
@@ -15,7 +15,7 @@ import {
15
  } from "@assistant-ui/react-markdown";
16
  import remarkGfm from "remark-gfm";
17
  import rehypeRaw from "rehype-raw";
18
- import { type FC, memo, useState } from "react";
19
  import { CheckIcon, CopyIcon } from "lucide-react";
20
 
21
  import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
@@ -23,11 +23,14 @@ import { cn } from "@/lib/utils";
23
 
24
  import { SyntaxHighlighter } from "./syntax-highlighter";
25
 
 
 
 
26
  const MarkdownTextImpl = () => {
27
  return (
28
  <MarkdownTextPrimitive
29
- remarkPlugins={[remarkGfm]}
30
- rehypePlugins={[rehypeRaw]}
31
  className="aui-md"
32
  components={defaultComponents}
33
  />
@@ -36,12 +39,13 @@ const MarkdownTextImpl = () => {
36
 
37
  export const MarkdownText = memo(MarkdownTextImpl);
38
 
39
- const CodeHeader: FC<CodeHeaderProps> = memo(({ language, code }) => {
40
  const { isCopied, copyToClipboard } = useCopyToClipboard();
41
- const onCopy = () => {
 
42
  if (!code || isCopied) return;
43
  copyToClipboard(code);
44
- };
45
 
46
  return (
47
  <div className="aui-code-header-root mt-4 flex items-center justify-between gap-4 rounded-t-lg bg-muted-foreground/15 px-4 py-2 text-sm font-semibold text-foreground dark:bg-muted-foreground/20">
@@ -54,8 +58,9 @@ const CodeHeader: FC<CodeHeaderProps> = memo(({ language, code }) => {
54
  </TooltipIconButton>
55
  </div>
56
  );
57
- });
58
 
 
59
  CodeHeader.displayName = "CodeHeader";
60
 
61
  const useCopyToClipboard = ({
@@ -65,14 +70,14 @@ const useCopyToClipboard = ({
65
  } = {}) => {
66
  const [isCopied, setIsCopied] = useState<boolean>(false);
67
 
68
- const copyToClipboard = (value: string) => {
69
  if (!value) return;
70
 
71
  navigator.clipboard.writeText(value).then(() => {
72
  setIsCopied(true);
73
  setTimeout(() => setIsCopied(false), copiedDuration);
74
  });
75
- };
76
 
77
  return { isCopied, copyToClipboard };
78
  };
@@ -94,171 +99,227 @@ const filterTableContent = (children: React.ReactNode): React.ReactNode => {
94
  return children;
95
  };
96
 
97
- const defaultComponents = memoizeMarkdownComponents({
98
- SyntaxHighlighter: MemoizedSyntaxHighlighter,
99
-
100
- h1: ({ className, ...props }) => (
101
- <h1
102
- className={cn(
103
- "aui-md-h1 mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
104
- className,
105
- )}
106
- {...props}
107
- />
108
- ),
109
- h2: ({ className, ...props }) => (
110
- <h2
111
- className={cn(
112
- "aui-md-h2 mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
113
- className,
114
- )}
115
- {...props}
116
- />
117
- ),
118
- h3: ({ className, ...props }) => (
119
- <h3
120
- className={cn(
121
- "aui-md-h3 mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
122
- className,
123
- )}
124
- {...props}
125
- />
126
- ),
127
- h4: ({ className, ...props }) => (
128
- <h4
129
- className={cn(
130
- "aui-md-h4 mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
131
- className,
132
- )}
133
- {...props}
134
- />
135
- ),
136
- h5: ({ className, ...props }) => (
137
- <h5
138
- className={cn(
139
- "aui-md-h5 my-4 text-lg font-semibold first:mt-0 last:mb-0",
140
- className,
141
- )}
142
- {...props}
143
- />
144
- ),
145
- h6: ({ className, ...props }) => (
146
- <h6
147
- className={cn(
148
- "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
149
- className,
150
- )}
151
- {...props}
152
- />
153
- ),
154
- p: ({ className, ...props }) => (
155
- <p
156
- className={cn(
157
- "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
158
- className,
159
- )}
160
- {...props}
161
- />
162
- ),
163
- a: ({ className, ...props }) => (
164
- <a
165
- className={cn(
166
- "aui-md-a font-medium text-primary underline underline-offset-4",
167
- className,
168
- )}
169
- {...props}
170
- />
171
- ),
172
- blockquote: ({ className, ...props }) => (
173
- <blockquote
174
- className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
175
- {...props}
176
- />
177
- ),
178
- ul: ({ className, ...props }) => (
179
- <ul
180
- className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
181
- {...props}
182
- />
183
- ),
184
- ol: ({ className, ...props }) => (
185
- <ol
186
- className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
187
- {...props}
188
- />
189
- ),
190
- hr: ({ className, ...props }) => (
191
- <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
192
- ),
193
- table: ({ className, ...props }) => (
194
- <TableWrapper>
195
- <table
196
- className={cn(
197
- "aui-md-table w-full min-w-full border-separate border-spacing-0",
198
- className,
199
- )}
200
- {...props}
201
- />
202
- </TableWrapper>
203
- ),
204
- th: ({ className, children, ...props }) => (
205
- <th
206
- className={cn(
207
- "aui-md-th bg-muted px-4 py-2 text-left font-bold whitespace-nowrap first:rounded-tl-lg last:rounded-tr-lg [&[align=center]]:text-center [&[align=right]]:text-right",
208
- className,
209
- )}
210
- {...props}
211
- >
212
- {filterTableContent(children)}
213
- </th>
214
- ),
215
- td: ({ className, children, ...props }) => (
216
- <td
217
- className={cn(
218
- "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
219
- className,
220
- )}
221
- {...props}
222
- >
223
- {filterTableContent(children)}
224
- </td>
225
- ),
226
- tr: ({ className, ...props }) => (
227
- <tr
228
  className={cn(
229
- "aui-md-tr m-0 border-b p-0 first:border-t [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg",
230
  className,
231
  )}
232
  {...props}
233
  />
234
- ),
235
- sup: ({ className, ...props }) => (
236
- <sup
237
- className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
238
- {...props}
239
- />
240
- ),
241
- pre: ({ className, ...props }) => (
242
- <pre
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  className={cn(
244
- "aui-md-pre overflow-x-auto !rounded-t-none rounded-b-lg bg-black p-4 text-white",
 
245
  className,
246
  )}
247
  {...props}
248
  />
249
- ),
250
- code: memo(function Code({ className, ...props }) {
251
- const isCodeBlock = useIsMarkdownCodeBlock();
252
- return (
253
- <code
254
- className={cn(
255
- !isCodeBlock &&
256
- "aui-md-inline-code rounded border bg-muted font-semibold px-1.5 py-0.5",
257
- className,
258
- )}
259
- {...props}
260
- />
261
- );
262
- }),
 
 
 
 
 
 
 
 
 
 
 
263
  CodeHeader,
264
  });
 
15
  } from "@assistant-ui/react-markdown";
16
  import remarkGfm from "remark-gfm";
17
  import rehypeRaw from "rehype-raw";
18
+ import { type FC, memo, useState, useCallback } from "react";
19
  import { CheckIcon, CopyIcon } from "lucide-react";
20
 
21
  import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
 
23
 
24
  import { SyntaxHighlighter } from "./syntax-highlighter";
25
 
26
+ const REMARK_PLUGINS = [remarkGfm];
27
+ const REHYPE_PLUGINS = [rehypeRaw];
28
+
29
  const MarkdownTextImpl = () => {
30
  return (
31
  <MarkdownTextPrimitive
32
+ remarkPlugins={REMARK_PLUGINS}
33
+ rehypePlugins={REHYPE_PLUGINS}
34
  className="aui-md"
35
  components={defaultComponents}
36
  />
 
39
 
40
  export const MarkdownText = memo(MarkdownTextImpl);
41
 
42
+ const CodeHeaderImpl: FC<CodeHeaderProps> = ({ language, code }) => {
43
  const { isCopied, copyToClipboard } = useCopyToClipboard();
44
+
45
+ const onCopy = useCallback(() => {
46
  if (!code || isCopied) return;
47
  copyToClipboard(code);
48
+ }, [code, isCopied, copyToClipboard]);
49
 
50
  return (
51
  <div className="aui-code-header-root mt-4 flex items-center justify-between gap-4 rounded-t-lg bg-muted-foreground/15 px-4 py-2 text-sm font-semibold text-foreground dark:bg-muted-foreground/20">
 
58
  </TooltipIconButton>
59
  </div>
60
  );
61
+ };
62
 
63
+ const CodeHeader = memo(CodeHeaderImpl);
64
  CodeHeader.displayName = "CodeHeader";
65
 
66
  const useCopyToClipboard = ({
 
70
  } = {}) => {
71
  const [isCopied, setIsCopied] = useState<boolean>(false);
72
 
73
+ const copyToClipboard = useCallback((value: string) => {
74
  if (!value) return;
75
 
76
  navigator.clipboard.writeText(value).then(() => {
77
  setIsCopied(true);
78
  setTimeout(() => setIsCopied(false), copiedDuration);
79
  });
80
+ }, [copiedDuration]);
81
 
82
  return { isCopied, copyToClipboard };
83
  };
 
99
  return children;
100
  };
101
 
102
+ const H1 = memo(({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
103
+ <h1
104
+ className={cn(
105
+ "aui-md-h1 mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
106
+ className,
107
+ )}
108
+ {...props}
109
+ />
110
+ ));
111
+ H1.displayName = "H1";
112
+
113
+ const H2 = memo(({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
114
+ <h2
115
+ className={cn(
116
+ "aui-md-h2 mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
117
+ className,
118
+ )}
119
+ {...props}
120
+ />
121
+ ));
122
+ H2.displayName = "H2";
123
+
124
+ const H3 = memo(({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
125
+ <h3
126
+ className={cn(
127
+ "aui-md-h3 mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
128
+ className,
129
+ )}
130
+ {...props}
131
+ />
132
+ ));
133
+ H3.displayName = "H3";
134
+
135
+ const H4 = memo(({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
136
+ <h4
137
+ className={cn(
138
+ "aui-md-h4 mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
139
+ className,
140
+ )}
141
+ {...props}
142
+ />
143
+ ));
144
+ H4.displayName = "H4";
145
+
146
+ const H5 = memo(({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
147
+ <h5
148
+ className={cn(
149
+ "aui-md-h5 my-4 text-lg font-semibold first:mt-0 last:mb-0",
150
+ className,
151
+ )}
152
+ {...props}
153
+ />
154
+ ));
155
+ H5.displayName = "H5";
156
+
157
+ const H6 = memo(({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
158
+ <h6
159
+ className={cn(
160
+ "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
161
+ className,
162
+ )}
163
+ {...props}
164
+ />
165
+ ));
166
+ H6.displayName = "H6";
167
+
168
+ const P = memo(({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
169
+ <p
170
+ className={cn(
171
+ "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
172
+ className,
173
+ )}
174
+ {...props}
175
+ />
176
+ ));
177
+ P.displayName = "P";
178
+
179
+ const A = memo(({ className, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
180
+ <a
181
+ className={cn(
182
+ "aui-md-a font-medium text-primary underline underline-offset-4",
183
+ className,
184
+ )}
185
+ {...props}
186
+ />
187
+ ));
188
+ A.displayName = "A";
189
+
190
+ const Blockquote = memo(({ className, ...props }: React.BlockquoteHTMLAttributes<HTMLQuoteElement>) => (
191
+ <blockquote
192
+ className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
193
+ {...props}
194
+ />
195
+ ));
196
+ Blockquote.displayName = "Blockquote";
197
+
198
+ const Ul = memo(({ className, ...props }: React.HTMLAttributes<HTMLUListElement>) => (
199
+ <ul
200
+ className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
201
+ {...props}
202
+ />
203
+ ));
204
+ Ul.displayName = "Ul";
205
+
206
+ const Ol = memo(({ className, ...props }: React.OlHTMLAttributes<HTMLOListElement>) => (
207
+ <ol
208
+ className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
209
+ {...props}
210
+ />
211
+ ));
212
+ Ol.displayName = "Ol";
213
+
214
+ const Hr = memo(({ className, ...props }: React.HTMLAttributes<HTMLHRElement>) => (
215
+ <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
216
+ ));
217
+ Hr.displayName = "Hr";
218
+
219
+ const Table = memo(({ className, ...props }: React.TableHTMLAttributes<HTMLTableElement>) => (
220
+ <TableWrapper>
221
+ <table
 
 
 
 
 
 
 
 
 
 
 
222
  className={cn(
223
+ "aui-md-table w-full min-w-full border-separate border-spacing-0",
224
  className,
225
  )}
226
  {...props}
227
  />
228
+ </TableWrapper>
229
+ ));
230
+ Table.displayName = "Table";
231
+
232
+ const Th = memo(({ className, children, ...props }: React.ThHTMLAttributes<HTMLTableCellElement>) => (
233
+ <th
234
+ className={cn(
235
+ "aui-md-th bg-muted px-4 py-2 text-left font-bold whitespace-nowrap first:rounded-tl-lg last:rounded-tr-lg [&[align=center]]:text-center [&[align=right]]:text-right",
236
+ className,
237
+ )}
238
+ {...props}
239
+ >
240
+ {filterTableContent(children)}
241
+ </th>
242
+ ));
243
+ Th.displayName = "Th";
244
+
245
+ const Td = memo(({ className, children, ...props }: React.TdHTMLAttributes<HTMLTableCellElement>) => (
246
+ <td
247
+ className={cn(
248
+ "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
249
+ className,
250
+ )}
251
+ {...props}
252
+ >
253
+ {filterTableContent(children)}
254
+ </td>
255
+ ));
256
+ Td.displayName = "Td";
257
+
258
+ const Tr = memo(({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
259
+ <tr
260
+ className={cn(
261
+ "aui-md-tr m-0 border-b p-0 first:border-t [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg",
262
+ className,
263
+ )}
264
+ {...props}
265
+ />
266
+ ));
267
+ Tr.displayName = "Tr";
268
+
269
+ const Sup = memo(({ className, ...props }: React.HTMLAttributes<HTMLElement>) => (
270
+ <sup
271
+ className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
272
+ {...props}
273
+ />
274
+ ));
275
+ Sup.displayName = "Sup";
276
+
277
+ const Pre = memo(({ className, ...props }: React.HTMLAttributes<HTMLPreElement>) => (
278
+ <pre
279
+ className={cn(
280
+ "aui-md-pre overflow-x-auto !rounded-t-none rounded-b-lg bg-black p-4 text-white",
281
+ className,
282
+ )}
283
+ {...props}
284
+ />
285
+ ));
286
+ Pre.displayName = "Pre";
287
+
288
+ const Code = memo(function Code({ className, ...props }: React.HTMLAttributes<HTMLElement>) {
289
+ const isCodeBlock = useIsMarkdownCodeBlock();
290
+ return (
291
+ <code
292
  className={cn(
293
+ !isCodeBlock &&
294
+ "aui-md-inline-code rounded border bg-muted font-semibold px-1.5 py-0.5",
295
  className,
296
  )}
297
  {...props}
298
  />
299
+ );
300
+ });
301
+ Code.displayName = "Code";
302
+
303
+ const defaultComponents = memoizeMarkdownComponents({
304
+ SyntaxHighlighter: MemoizedSyntaxHighlighter,
305
+ h1: H1,
306
+ h2: H2,
307
+ h3: H3,
308
+ h4: H4,
309
+ h5: H5,
310
+ h6: H6,
311
+ p: P,
312
+ a: A,
313
+ blockquote: Blockquote,
314
+ ul: Ul,
315
+ ol: Ol,
316
+ hr: Hr,
317
+ table: Table,
318
+ th: Th,
319
+ td: Td,
320
+ tr: Tr,
321
+ sup: Sup,
322
+ pre: Pre,
323
+ code: Code,
324
  CodeHeader,
325
  });
components/ui/sidebar.tsx CHANGED
@@ -1,3 +1,8 @@
 
 
 
 
 
1
  "use client";
2
 
3
  import * as React from "react";
@@ -53,7 +58,7 @@ function useSidebar() {
53
  return context;
54
  }
55
 
56
- function SidebarProvider({
57
  defaultOpen = true,
58
  open: openProp,
59
  onOpenChange: setOpenProp,
@@ -68,11 +73,10 @@ function SidebarProvider({
68
  }) {
69
  const isMobile = useIsMobile();
70
  const [openMobile, setOpenMobile] = React.useState(false);
71
-
72
- // This is the internal state of the sidebar.
73
- // We use openProp and setOpenProp for control from outside the component.
74
  const [_open, _setOpen] = React.useState(defaultOpen);
 
75
  const open = openProp ?? _open;
 
76
  const setOpen = React.useCallback(
77
  (value: boolean | ((value: boolean) => boolean)) => {
78
  const openState = typeof value === "function" ? value(open) : value;
@@ -82,18 +86,15 @@ function SidebarProvider({
82
  _setOpen(openState);
83
  }
84
 
85
- // This sets the cookie to keep the sidebar state.
86
  document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
87
  },
88
  [setOpenProp, open],
89
  );
90
 
91
- // Helper to toggle the sidebar.
92
  const toggleSidebar = React.useCallback(() => {
93
  return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
94
- }, [isMobile, setOpen, setOpenMobile]);
95
 
96
- // Adds a keyboard shortcut to toggle the sidebar.
97
  React.useEffect(() => {
98
  const handleKeyDown = (event: KeyboardEvent) => {
99
  if (
@@ -109,8 +110,6 @@ function SidebarProvider({
109
  return () => window.removeEventListener("keydown", handleKeyDown);
110
  }, [toggleSidebar]);
111
 
112
- // We add a state so that we can do data-state="expanded" or "collapsed".
113
- // This makes it easier to style the sidebar with Tailwind classes.
114
  const state = open ? "expanded" : "collapsed";
115
 
116
  const contextValue = React.useMemo<SidebarContextProps>(
@@ -123,7 +122,17 @@ function SidebarProvider({
123
  setOpenMobile,
124
  toggleSidebar,
125
  }),
126
- [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],
 
 
 
 
 
 
 
 
 
 
127
  );
128
 
129
  return (
@@ -131,13 +140,7 @@ function SidebarProvider({
131
  <TooltipProvider delayDuration={0}>
132
  <div
133
  data-slot="sidebar-wrapper"
134
- style={
135
- {
136
- "--sidebar-width": SIDEBAR_WIDTH,
137
- "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
138
- ...style,
139
- } as React.CSSProperties
140
- }
141
  className={cn(
142
  "group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar",
143
  className,
@@ -149,9 +152,9 @@ function SidebarProvider({
149
  </TooltipProvider>
150
  </SidebarContext.Provider>
151
  );
152
- }
153
 
154
- function Sidebar({
155
  side = "left",
156
  variant = "sidebar",
157
  collapsible = "offcanvas",
@@ -165,6 +168,14 @@ function Sidebar({
165
  }) {
166
  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
167
 
 
 
 
 
 
 
 
 
168
  if (collapsible === "none") {
169
  return (
170
  <div
@@ -188,11 +199,7 @@ function Sidebar({
188
  data-slot="sidebar"
189
  data-mobile="true"
190
  className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
191
- style={
192
- {
193
- "--sidebar-width": SIDEBAR_WIDTH_MOBILE,
194
- } as React.CSSProperties
195
- }
196
  side={side}
197
  >
198
  <SheetHeader className="sr-only">
@@ -214,7 +221,6 @@ function Sidebar({
214
  data-side={side}
215
  data-slot="sidebar"
216
  >
217
- {/* This is what handles the sidebar gap on desktop */}
218
  <div
219
  data-slot="sidebar-gap"
220
  className={cn(
@@ -233,7 +239,6 @@ function Sidebar({
233
  side === "left"
234
  ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
235
  : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
236
- // Adjust the padding for floating and inset variants.
237
  variant === "floating" || variant === "inset"
238
  ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
239
  : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
@@ -251,15 +256,23 @@ function Sidebar({
251
  </div>
252
  </div>
253
  );
254
- }
255
 
256
- function SidebarTrigger({
257
  className,
258
  onClick,
259
  ...props
260
  }: React.ComponentProps<typeof Button>) {
261
  const { toggleSidebar } = useSidebar();
262
 
 
 
 
 
 
 
 
 
263
  return (
264
  <Button
265
  data-sidebar="trigger"
@@ -267,19 +280,19 @@ function SidebarTrigger({
267
  variant="ghost"
268
  size="icon"
269
  className={cn("size-7", className)}
270
- onClick={(event) => {
271
- onClick?.(event);
272
- toggleSidebar();
273
- }}
274
  {...props}
275
  >
276
  <PanelLeftIcon />
277
  <span className="sr-only">Toggle Sidebar</span>
278
  </Button>
279
  );
280
- }
281
 
282
- function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
 
 
 
283
  const { toggleSidebar } = useSidebar();
284
 
285
  return (
@@ -302,9 +315,12 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
302
  {...props}
303
  />
304
  );
305
- }
306
 
307
- function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
 
 
 
308
  return (
309
  <main
310
  data-slot="sidebar-inset"
@@ -316,9 +332,9 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
316
  {...props}
317
  />
318
  );
319
- }
320
 
321
- function SidebarInput({
322
  className,
323
  ...props
324
  }: React.ComponentProps<typeof Input>) {
@@ -330,9 +346,12 @@ function SidebarInput({
330
  {...props}
331
  />
332
  );
333
- }
334
 
335
- function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
 
 
 
336
  return (
337
  <div
338
  data-slot="sidebar-header"
@@ -341,9 +360,12 @@ function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
341
  {...props}
342
  />
343
  );
344
- }
345
 
346
- function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
 
 
 
347
  return (
348
  <div
349
  data-slot="sidebar-footer"
@@ -352,9 +374,9 @@ function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
352
  {...props}
353
  />
354
  );
355
- }
356
 
357
- function SidebarSeparator({
358
  className,
359
  ...props
360
  }: React.ComponentProps<typeof Separator>) {
@@ -366,9 +388,12 @@ function SidebarSeparator({
366
  {...props}
367
  />
368
  );
369
- }
370
 
371
- function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
 
 
 
372
  return (
373
  <div
374
  data-slot="sidebar-content"
@@ -380,9 +405,12 @@ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
380
  {...props}
381
  />
382
  );
383
- }
384
 
385
- function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
 
 
 
386
  return (
387
  <div
388
  data-slot="sidebar-group"
@@ -391,9 +419,9 @@ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
391
  {...props}
392
  />
393
  );
394
- }
395
 
396
- function SidebarGroupLabel({
397
  className,
398
  asChild = false,
399
  ...props
@@ -412,9 +440,9 @@ function SidebarGroupLabel({
412
  {...props}
413
  />
414
  );
415
- }
416
 
417
- function SidebarGroupAction({
418
  className,
419
  asChild = false,
420
  ...props
@@ -427,7 +455,6 @@ function SidebarGroupAction({
427
  data-sidebar="group-action"
428
  className={cn(
429
  "absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
430
- // Increases the hit area of the button on mobile.
431
  "after:absolute after:-inset-2 md:after:hidden",
432
  "group-data-[collapsible=icon]:hidden",
433
  className,
@@ -435,9 +462,9 @@ function SidebarGroupAction({
435
  {...props}
436
  />
437
  );
438
- }
439
 
440
- function SidebarGroupContent({
441
  className,
442
  ...props
443
  }: React.ComponentProps<"div">) {
@@ -449,9 +476,12 @@ function SidebarGroupContent({
449
  {...props}
450
  />
451
  );
452
- }
453
 
454
- function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
 
 
 
455
  return (
456
  <ul
457
  data-slot="sidebar-menu"
@@ -460,9 +490,12 @@ function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
460
  {...props}
461
  />
462
  );
463
- }
464
 
465
- function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
 
 
 
466
  return (
467
  <li
468
  data-slot="sidebar-menu-item"
@@ -471,7 +504,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
471
  {...props}
472
  />
473
  );
474
- }
475
 
476
  const sidebarMenuButtonVariants = cva(
477
  "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
@@ -495,7 +528,7 @@ const sidebarMenuButtonVariants = cva(
495
  },
496
  );
497
 
498
- function SidebarMenuButton({
499
  asChild = false,
500
  isActive = false,
501
  variant = "default",
@@ -526,11 +559,7 @@ function SidebarMenuButton({
526
  return button;
527
  }
528
 
529
- if (typeof tooltip === "string") {
530
- tooltip = {
531
- children: tooltip,
532
- };
533
- }
534
 
535
  return (
536
  <Tooltip>
@@ -539,13 +568,13 @@ function SidebarMenuButton({
539
  side="right"
540
  align="center"
541
  hidden={state !== "collapsed" || isMobile}
542
- {...tooltip}
543
  />
544
  </Tooltip>
545
  );
546
- }
547
 
548
- function SidebarMenuAction({
549
  className,
550
  asChild = false,
551
  showOnHover = false,
@@ -562,7 +591,6 @@ function SidebarMenuAction({
562
  data-sidebar="menu-action"
563
  className={cn(
564
  "absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform peer-hover/menu-button:text-sidebar-accent-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
565
- // Increases the hit area of the button on mobile.
566
  "after:absolute after:-inset-2 md:after:hidden",
567
  "peer-data-[size=sm]/menu-button:top-1",
568
  "peer-data-[size=default]/menu-button:top-1.5",
@@ -575,9 +603,9 @@ function SidebarMenuAction({
575
  {...props}
576
  />
577
  );
578
- }
579
 
580
- function SidebarMenuBadge({
581
  className,
582
  ...props
583
  }: React.ComponentProps<"div">) {
@@ -597,20 +625,27 @@ function SidebarMenuBadge({
597
  {...props}
598
  />
599
  );
600
- }
601
 
602
- function SidebarMenuSkeleton({
603
  className,
604
  showIcon = false,
605
  ...props
606
  }: React.ComponentProps<"div"> & {
607
  showIcon?: boolean;
608
  }) {
609
- // Random width between 50 to 90%.
610
  const width = React.useMemo(() => {
611
  return `${Math.floor(Math.random() * 40) + 50}%`;
612
  }, []);
613
 
 
 
 
 
 
 
 
 
614
  return (
615
  <div
616
  data-slot="sidebar-menu-skeleton"
@@ -627,17 +662,16 @@ function SidebarMenuSkeleton({
627
  <Skeleton
628
  className="h-4 max-w-(--skeleton-width) flex-1"
629
  data-sidebar="menu-skeleton-text"
630
- style={
631
- {
632
- "--skeleton-width": width,
633
- } as React.CSSProperties
634
- }
635
  />
636
  </div>
637
  );
638
- }
639
 
640
- function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
 
 
 
641
  return (
642
  <ul
643
  data-slot="sidebar-menu-sub"
@@ -650,9 +684,9 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
650
  {...props}
651
  />
652
  );
653
- }
654
 
655
- function SidebarMenuSubItem({
656
  className,
657
  ...props
658
  }: React.ComponentProps<"li">) {
@@ -664,9 +698,9 @@ function SidebarMenuSubItem({
664
  {...props}
665
  />
666
  );
667
- }
668
 
669
- function SidebarMenuSubButton({
670
  asChild = false,
671
  size = "md",
672
  isActive = false,
@@ -696,7 +730,7 @@ function SidebarMenuSubButton({
696
  {...props}
697
  />
698
  );
699
- }
700
 
701
  export {
702
  Sidebar,
 
1
+ //
2
+ // SPDX-FileCopyrightText: Hadad <hadad@linuxmail.org>
3
+ // SPDX-License-Identifier: MIT
4
+ //
5
+
6
  "use client";
7
 
8
  import * as React from "react";
 
58
  return context;
59
  }
60
 
61
+ const SidebarProvider = React.memo(function SidebarProvider({
62
  defaultOpen = true,
63
  open: openProp,
64
  onOpenChange: setOpenProp,
 
73
  }) {
74
  const isMobile = useIsMobile();
75
  const [openMobile, setOpenMobile] = React.useState(false);
 
 
 
76
  const [_open, _setOpen] = React.useState(defaultOpen);
77
+
78
  const open = openProp ?? _open;
79
+
80
  const setOpen = React.useCallback(
81
  (value: boolean | ((value: boolean) => boolean)) => {
82
  const openState = typeof value === "function" ? value(open) : value;
 
86
  _setOpen(openState);
87
  }
88
 
 
89
  document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
90
  },
91
  [setOpenProp, open],
92
  );
93
 
 
94
  const toggleSidebar = React.useCallback(() => {
95
  return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
96
+ }, [isMobile, setOpen]);
97
 
 
98
  React.useEffect(() => {
99
  const handleKeyDown = (event: KeyboardEvent) => {
100
  if (
 
110
  return () => window.removeEventListener("keydown", handleKeyDown);
111
  }, [toggleSidebar]);
112
 
 
 
113
  const state = open ? "expanded" : "collapsed";
114
 
115
  const contextValue = React.useMemo<SidebarContextProps>(
 
122
  setOpenMobile,
123
  toggleSidebar,
124
  }),
125
+ [state, open, setOpen, isMobile, openMobile, toggleSidebar],
126
+ );
127
+
128
+ const inlineStyle = React.useMemo(
129
+ () =>
130
+ ({
131
+ "--sidebar-width": SIDEBAR_WIDTH,
132
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
133
+ ...style,
134
+ } as React.CSSProperties),
135
+ [style],
136
  );
137
 
138
  return (
 
140
  <TooltipProvider delayDuration={0}>
141
  <div
142
  data-slot="sidebar-wrapper"
143
+ style={inlineStyle}
 
 
 
 
 
 
144
  className={cn(
145
  "group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar",
146
  className,
 
152
  </TooltipProvider>
153
  </SidebarContext.Provider>
154
  );
155
+ });
156
 
157
+ const Sidebar = React.memo(function Sidebar({
158
  side = "left",
159
  variant = "sidebar",
160
  collapsible = "offcanvas",
 
168
  }) {
169
  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
170
 
171
+ const sheetStyle = React.useMemo(
172
+ () =>
173
+ ({
174
+ "--sidebar-width": SIDEBAR_WIDTH_MOBILE,
175
+ } as React.CSSProperties),
176
+ [],
177
+ );
178
+
179
  if (collapsible === "none") {
180
  return (
181
  <div
 
199
  data-slot="sidebar"
200
  data-mobile="true"
201
  className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
202
+ style={sheetStyle}
 
 
 
 
203
  side={side}
204
  >
205
  <SheetHeader className="sr-only">
 
221
  data-side={side}
222
  data-slot="sidebar"
223
  >
 
224
  <div
225
  data-slot="sidebar-gap"
226
  className={cn(
 
239
  side === "left"
240
  ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
241
  : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
 
242
  variant === "floating" || variant === "inset"
243
  ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
244
  : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
 
256
  </div>
257
  </div>
258
  );
259
+ });
260
 
261
+ const SidebarTrigger = React.memo(function SidebarTrigger({
262
  className,
263
  onClick,
264
  ...props
265
  }: React.ComponentProps<typeof Button>) {
266
  const { toggleSidebar } = useSidebar();
267
 
268
+ const handleClick = React.useCallback(
269
+ (event: React.MouseEvent<HTMLButtonElement>) => {
270
+ onClick?.(event);
271
+ toggleSidebar();
272
+ },
273
+ [onClick, toggleSidebar],
274
+ );
275
+
276
  return (
277
  <Button
278
  data-sidebar="trigger"
 
280
  variant="ghost"
281
  size="icon"
282
  className={cn("size-7", className)}
283
+ onClick={handleClick}
 
 
 
284
  {...props}
285
  >
286
  <PanelLeftIcon />
287
  <span className="sr-only">Toggle Sidebar</span>
288
  </Button>
289
  );
290
+ });
291
 
292
+ const SidebarRail = React.memo(function SidebarRail({
293
+ className,
294
+ ...props
295
+ }: React.ComponentProps<"button">) {
296
  const { toggleSidebar } = useSidebar();
297
 
298
  return (
 
315
  {...props}
316
  />
317
  );
318
+ });
319
 
320
+ const SidebarInset = React.memo(function SidebarInset({
321
+ className,
322
+ ...props
323
+ }: React.ComponentProps<"main">) {
324
  return (
325
  <main
326
  data-slot="sidebar-inset"
 
332
  {...props}
333
  />
334
  );
335
+ });
336
 
337
+ const SidebarInput = React.memo(function SidebarInput({
338
  className,
339
  ...props
340
  }: React.ComponentProps<typeof Input>) {
 
346
  {...props}
347
  />
348
  );
349
+ });
350
 
351
+ const SidebarHeader = React.memo(function SidebarHeader({
352
+ className,
353
+ ...props
354
+ }: React.ComponentProps<"div">) {
355
  return (
356
  <div
357
  data-slot="sidebar-header"
 
360
  {...props}
361
  />
362
  );
363
+ });
364
 
365
+ const SidebarFooter = React.memo(function SidebarFooter({
366
+ className,
367
+ ...props
368
+ }: React.ComponentProps<"div">) {
369
  return (
370
  <div
371
  data-slot="sidebar-footer"
 
374
  {...props}
375
  />
376
  );
377
+ });
378
 
379
+ const SidebarSeparator = React.memo(function SidebarSeparator({
380
  className,
381
  ...props
382
  }: React.ComponentProps<typeof Separator>) {
 
388
  {...props}
389
  />
390
  );
391
+ });
392
 
393
+ const SidebarContent = React.memo(function SidebarContent({
394
+ className,
395
+ ...props
396
+ }: React.ComponentProps<"div">) {
397
  return (
398
  <div
399
  data-slot="sidebar-content"
 
405
  {...props}
406
  />
407
  );
408
+ });
409
 
410
+ const SidebarGroup = React.memo(function SidebarGroup({
411
+ className,
412
+ ...props
413
+ }: React.ComponentProps<"div">) {
414
  return (
415
  <div
416
  data-slot="sidebar-group"
 
419
  {...props}
420
  />
421
  );
422
+ });
423
 
424
+ const SidebarGroupLabel = React.memo(function SidebarGroupLabel({
425
  className,
426
  asChild = false,
427
  ...props
 
440
  {...props}
441
  />
442
  );
443
+ });
444
 
445
+ const SidebarGroupAction = React.memo(function SidebarGroupAction({
446
  className,
447
  asChild = false,
448
  ...props
 
455
  data-sidebar="group-action"
456
  className={cn(
457
  "absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
 
458
  "after:absolute after:-inset-2 md:after:hidden",
459
  "group-data-[collapsible=icon]:hidden",
460
  className,
 
462
  {...props}
463
  />
464
  );
465
+ });
466
 
467
+ const SidebarGroupContent = React.memo(function SidebarGroupContent({
468
  className,
469
  ...props
470
  }: React.ComponentProps<"div">) {
 
476
  {...props}
477
  />
478
  );
479
+ });
480
 
481
+ const SidebarMenu = React.memo(function SidebarMenu({
482
+ className,
483
+ ...props
484
+ }: React.ComponentProps<"ul">) {
485
  return (
486
  <ul
487
  data-slot="sidebar-menu"
 
490
  {...props}
491
  />
492
  );
493
+ });
494
 
495
+ const SidebarMenuItem = React.memo(function SidebarMenuItem({
496
+ className,
497
+ ...props
498
+ }: React.ComponentProps<"li">) {
499
  return (
500
  <li
501
  data-slot="sidebar-menu-item"
 
504
  {...props}
505
  />
506
  );
507
+ });
508
 
509
  const sidebarMenuButtonVariants = cva(
510
  "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
 
528
  },
529
  );
530
 
531
+ const SidebarMenuButton = React.memo(function SidebarMenuButton({
532
  asChild = false,
533
  isActive = false,
534
  variant = "default",
 
559
  return button;
560
  }
561
 
562
+ const tooltipProps = typeof tooltip === "string" ? { children: tooltip } : tooltip;
 
 
 
 
563
 
564
  return (
565
  <Tooltip>
 
568
  side="right"
569
  align="center"
570
  hidden={state !== "collapsed" || isMobile}
571
+ {...tooltipProps}
572
  />
573
  </Tooltip>
574
  );
575
+ });
576
 
577
+ const SidebarMenuAction = React.memo(function SidebarMenuAction({
578
  className,
579
  asChild = false,
580
  showOnHover = false,
 
591
  data-sidebar="menu-action"
592
  className={cn(
593
  "absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform peer-hover/menu-button:text-sidebar-accent-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
 
594
  "after:absolute after:-inset-2 md:after:hidden",
595
  "peer-data-[size=sm]/menu-button:top-1",
596
  "peer-data-[size=default]/menu-button:top-1.5",
 
603
  {...props}
604
  />
605
  );
606
+ });
607
 
608
+ const SidebarMenuBadge = React.memo(function SidebarMenuBadge({
609
  className,
610
  ...props
611
  }: React.ComponentProps<"div">) {
 
625
  {...props}
626
  />
627
  );
628
+ });
629
 
630
+ const SidebarMenuSkeleton = React.memo(function SidebarMenuSkeleton({
631
  className,
632
  showIcon = false,
633
  ...props
634
  }: React.ComponentProps<"div"> & {
635
  showIcon?: boolean;
636
  }) {
 
637
  const width = React.useMemo(() => {
638
  return `${Math.floor(Math.random() * 40) + 50}%`;
639
  }, []);
640
 
641
+ const skeletonStyle = React.useMemo(
642
+ () =>
643
+ ({
644
+ "--skeleton-width": width,
645
+ } as React.CSSProperties),
646
+ [width],
647
+ );
648
+
649
  return (
650
  <div
651
  data-slot="sidebar-menu-skeleton"
 
662
  <Skeleton
663
  className="h-4 max-w-(--skeleton-width) flex-1"
664
  data-sidebar="menu-skeleton-text"
665
+ style={skeletonStyle}
 
 
 
 
666
  />
667
  </div>
668
  );
669
+ });
670
 
671
+ const SidebarMenuSub = React.memo(function SidebarMenuSub({
672
+ className,
673
+ ...props
674
+ }: React.ComponentProps<"ul">) {
675
  return (
676
  <ul
677
  data-slot="sidebar-menu-sub"
 
684
  {...props}
685
  />
686
  );
687
+ });
688
 
689
+ const SidebarMenuSubItem = React.memo(function SidebarMenuSubItem({
690
  className,
691
  ...props
692
  }: React.ComponentProps<"li">) {
 
698
  {...props}
699
  />
700
  );
701
+ });
702
 
703
+ const SidebarMenuSubButton = React.memo(function SidebarMenuSubButton({
704
  asChild = false,
705
  size = "md",
706
  isActive = false,
 
730
  {...props}
731
  />
732
  );
733
+ });
734
 
735
  export {
736
  Sidebar,
config.ts CHANGED
@@ -7,7 +7,7 @@ export default {
7
  api: {
8
  baseUrl: process.env.OPENAI_API_BASE_URL, // /v1
9
  key: process.env.OPENAI_API_KEY,
10
- model: "openai/gpt-5-chat-latest",
11
  maxRetries: 5,
12
  timeout: 20000 // 20 seconds
13
  }
 
7
  api: {
8
  baseUrl: process.env.OPENAI_API_BASE_URL, // /v1
9
  key: process.env.OPENAI_API_KEY,
10
+ model: "limited/gpt-5-chat-latest",
11
  maxRetries: 5,
12
  timeout: 20000 // 20 seconds
13
  }