@tscircuit/fake-snippets 0.0.87 → 0.0.89

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.
Files changed (75) hide show
  1. package/api/generated-index.js +96 -14
  2. package/bun-tests/fake-snippets-api/routes/proxy.test.ts +42 -0
  3. package/bun.lock +187 -206
  4. package/dist/bundle.js +207 -101
  5. package/fake-snippets-api/routes/api/package_releases/create.ts +1 -1
  6. package/fake-snippets-api/routes/api/proxy.ts +128 -0
  7. package/package.json +57 -50
  8. package/renovate.json +2 -1
  9. package/src/App.tsx +22 -3
  10. package/src/ContextProviders.tsx +2 -0
  11. package/src/build-watcher.ts +52 -0
  12. package/src/components/CmdKMenu.tsx +533 -197
  13. package/src/components/DownloadButtonAndMenu.tsx +104 -26
  14. package/src/components/FileSidebar.tsx +11 -1
  15. package/src/components/Header.tsx +5 -1
  16. package/src/components/Header2.tsx +7 -2
  17. package/src/components/HeaderLogin.tsx +1 -1
  18. package/src/components/PackageBuildsPage/LogContent.tsx +25 -22
  19. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +6 -6
  20. package/src/components/PackageBuildsPage/build-preview-content.tsx +5 -5
  21. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +15 -13
  22. package/src/components/PackageBuildsPage/package-build-header.tsx +17 -16
  23. package/src/components/PackageCard.tsx +66 -16
  24. package/src/components/PrefetchPageLink.tsx +66 -15
  25. package/src/components/SearchComponent.tsx +2 -2
  26. package/src/components/SuspenseRunFrame.tsx +14 -2
  27. package/src/components/ViewPackagePage/components/important-files-view.tsx +97 -22
  28. package/src/components/ViewPackagePage/components/main-content-header.tsx +27 -3
  29. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +2 -2
  30. package/src/components/ViewPackagePage/components/repo-page-content.tsx +49 -34
  31. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +2 -2
  32. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +20 -12
  33. package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +0 -7
  34. package/src/components/ViewPackagePage/utils/fuzz-search.ts +121 -0
  35. package/src/components/ViewPackagePage/utils/is-hidden-file.ts +4 -0
  36. package/src/components/ViewPackagePage/utils/is-package-file-important.ts +18 -5
  37. package/src/components/dialogs/confirm-delete-package-dialog.tsx +1 -1
  38. package/src/components/dialogs/confirm-discard-changes-dialog.tsx +73 -0
  39. package/src/components/dialogs/edit-package-details-dialog.tsx +2 -2
  40. package/src/components/dialogs/view-ts-files-dialog.tsx +478 -42
  41. package/src/components/package-port/CodeAndPreview.tsx +16 -0
  42. package/src/components/package-port/CodeEditor.tsx +113 -11
  43. package/src/components/package-port/CodeEditorHeader.tsx +39 -4
  44. package/src/components/package-port/EditorNav.tsx +41 -15
  45. package/src/components/package-port/GlobalFindReplace.tsx +681 -0
  46. package/src/components/package-port/QuickOpen.tsx +241 -0
  47. package/src/components/ui/dialog.tsx +1 -1
  48. package/src/components/ui/tree-view.tsx +1 -1
  49. package/src/global.d.ts +3 -0
  50. package/src/hooks/use-ai-review.ts +31 -0
  51. package/src/hooks/use-current-package-release.ts +5 -1
  52. package/src/hooks/use-download-zip.ts +50 -0
  53. package/src/hooks/use-hotkey.ts +116 -0
  54. package/src/hooks/use-package-by-package-id.ts +1 -0
  55. package/src/hooks/use-package-by-package-name.ts +1 -0
  56. package/src/hooks/use-package-files.ts +3 -0
  57. package/src/hooks/use-package-release.ts +5 -1
  58. package/src/hooks/use-package.ts +1 -0
  59. package/src/hooks/use-request-ai-review-mutation.ts +14 -6
  60. package/src/hooks/use-snippet.ts +1 -0
  61. package/src/hooks/useFileManagement.ts +26 -8
  62. package/src/hooks/usePackageFilesLoader.ts +3 -1
  63. package/src/index.css +11 -0
  64. package/src/lib/decodeUrlHashToFsMap.ts +17 -0
  65. package/src/lib/download-fns/download-circuit-png.ts +88 -0
  66. package/src/lib/download-fns/download-png-utils.ts +31 -0
  67. package/src/lib/encodeFsMapToUrlHash.ts +13 -0
  68. package/src/lib/populate-query-cache-with-ssr-data.ts +39 -38
  69. package/src/lib/ts-lib-cache.ts +47 -0
  70. package/src/lib/types.ts +2 -0
  71. package/src/main.tsx +7 -0
  72. package/src/pages/dashboard.tsx +8 -5
  73. package/src/pages/user-profile.tsx +1 -1
  74. package/src/pages/view-package.tsx +15 -7
  75. package/vite.config.ts +100 -1
@@ -1,7 +1,12 @@
1
1
  import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
2
+ import { useHotkeyCombo } from "@/hooks/use-hotkey"
2
3
  import { basicSetup } from "@/lib/codemirror/basic-setup"
3
- import { autocompletion } from "@codemirror/autocomplete"
4
- import { indentWithTab } from "@codemirror/commands"
4
+ import {
5
+ autocompletion,
6
+ acceptCompletion,
7
+ completionStatus,
8
+ } from "@codemirror/autocomplete"
9
+ import { indentWithTab, indentMore } from "@codemirror/commands"
5
10
  import { javascript } from "@codemirror/lang-javascript"
6
11
  import { json } from "@codemirror/lang-json"
7
12
  import { EditorState, Prec } from "@codemirror/state"
@@ -12,10 +17,10 @@ import { setupTypeAcquisition } from "@typescript/ata"
12
17
  import { linter } from "@codemirror/lint"
13
18
  import { TSCI_PACKAGE_PATTERN } from "@/lib/constants"
14
19
  import {
15
- createDefaultMapFromCDN,
16
20
  createSystem,
17
21
  createVirtualTypeScriptEnvironment,
18
22
  } from "@typescript/vfs"
23
+ import { loadDefaultLibMap } from "@/lib/ts-lib-cache"
19
24
  import { tsAutocomplete, tsFacet, tsSync } from "@valtown/codemirror-ts"
20
25
  import { getLints } from "@valtown/codemirror-ts"
21
26
  import { EditorView } from "codemirror"
@@ -29,12 +34,15 @@ import FileSidebar from "../FileSidebar"
29
34
  import { findTargetFile } from "@/lib/utils/findTargetFile"
30
35
  import type { PackageFile } from "./CodeAndPreview"
31
36
  import { useShikiHighlighter } from "@/hooks/use-shiki-highlighter"
37
+ import QuickOpen from "./QuickOpen"
38
+ import GlobalFindReplace from "./GlobalFindReplace"
32
39
  import {
33
40
  ICreateFileProps,
34
41
  ICreateFileResult,
35
42
  IDeleteFileProps,
36
43
  IDeleteFileResult,
37
44
  } from "@/hooks/useFileManagement"
45
+ import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
38
46
 
39
47
  const defaultImports = `
40
48
  import React from "@types/react/jsx-runtime"
@@ -77,6 +85,9 @@ export const CodeEditor = ({
77
85
  const codeCompletionApi = useCodeCompletionApi()
78
86
  const [cursorPosition, setCursorPosition] = useState<number | null>(null)
79
87
  const [code, setCode] = useState(files[0]?.content || "")
88
+ const [fontSize, setFontSize] = useState(14)
89
+ const [showQuickOpen, setShowQuickOpen] = useState(false)
90
+ const [showGlobalFindReplace, setShowGlobalFindReplace] = useState(false)
80
91
 
81
92
  const { highlighter } = useShikiHighlighter()
82
93
 
@@ -132,6 +143,14 @@ export const CodeEditor = ({
132
143
  }
133
144
  }, [isStreaming])
134
145
 
146
+ useHotkeyCombo(
147
+ "cmd+b",
148
+ () => {
149
+ setSidebarOpen((prev) => !prev)
150
+ },
151
+ { target: window },
152
+ )
153
+
135
154
  useEffect(() => {
136
155
  if (!editorRef.current) return
137
156
 
@@ -141,12 +160,7 @@ export const CodeEditor = ({
141
160
  })
142
161
  ;(window as any).__DEBUG_CODE_EDITOR_FS_MAP = fsMap
143
162
 
144
- createDefaultMapFromCDN(
145
- { target: tsModule.ScriptTarget.ES2022 },
146
- "5.6.3",
147
- true,
148
- tsModule,
149
- ).then((defaultFsMap) => {
163
+ loadDefaultLibMap().then((defaultFsMap) => {
150
164
  defaultFsMap.forEach((content, filename) => {
151
165
  fsMap.set(filename, content)
152
166
  })
@@ -243,6 +257,29 @@ export const CodeEditor = ({
243
257
  key: "Mod-Enter",
244
258
  run: () => true,
245
259
  },
260
+ {
261
+ key: "Tab",
262
+ run: (view) => {
263
+ if (completionStatus(view.state) === "active") {
264
+ return acceptCompletion(view)
265
+ }
266
+ return indentMore(view)
267
+ },
268
+ },
269
+ {
270
+ key: "Mod-p",
271
+ run: () => {
272
+ setShowQuickOpen(true)
273
+ return true
274
+ },
275
+ },
276
+ {
277
+ key: "Mod-Shift-f",
278
+ run: () => {
279
+ setShowGlobalFindReplace(true)
280
+ return true
281
+ },
282
+ },
246
283
  ]),
247
284
  ),
248
285
  keymap.of([indentWithTab]),
@@ -263,6 +300,29 @@ export const CodeEditor = ({
263
300
  setCursorPosition(pos)
264
301
  }
265
302
  }),
303
+ EditorView.theme({
304
+ ".cm-editor": {
305
+ fontSize: `${fontSize}px`,
306
+ },
307
+ ".cm-content": {
308
+ fontSize: `${fontSize}px`,
309
+ },
310
+ }),
311
+ EditorView.domEventHandlers({
312
+ wheel: (event) => {
313
+ if (event.ctrlKey || event.metaKey) {
314
+ event.preventDefault()
315
+ const delta = event.deltaY
316
+ setFontSize((prev) => {
317
+ const newSize =
318
+ delta > 0 ? Math.max(8, prev - 1) : Math.min(32, prev + 1)
319
+ return newSize
320
+ })
321
+ return true
322
+ }
323
+ return false
324
+ },
325
+ }),
266
326
  ]
267
327
  if (codeCompletionApi?.apiKey) {
268
328
  baseExtensions.push(
@@ -470,7 +530,14 @@ export const CodeEditor = ({
470
530
  return () => {
471
531
  view.destroy()
472
532
  }
473
- }, [!isStreaming, currentFile, code !== "", Boolean(highlighter), isSaving])
533
+ }, [
534
+ !isStreaming,
535
+ currentFile,
536
+ code !== "",
537
+ Boolean(highlighter),
538
+ isSaving,
539
+ fontSize,
540
+ ])
474
541
 
475
542
  const updateCurrentEditorContent = (newContent: string) => {
476
543
  if (viewRef.current) {
@@ -541,12 +608,30 @@ export const CodeEditor = ({
541
608
  updateEditorToMatchCurrentFile()
542
609
  }, [currentFile])
543
610
 
611
+ // Global keyboard listeners
612
+ useHotkeyCombo("cmd+p", () => {
613
+ setShowQuickOpen(true)
614
+ })
615
+
616
+ useHotkeyCombo("cmd+shift+f", () => {
617
+ setShowGlobalFindReplace(true)
618
+ })
619
+
620
+ useHotkeyCombo("Escape", () => {
621
+ if (showQuickOpen) {
622
+ setShowQuickOpen(false)
623
+ }
624
+ if (showGlobalFindReplace) {
625
+ setShowGlobalFindReplace(false)
626
+ }
627
+ })
628
+
544
629
  if (isStreaming) {
545
630
  return <div className="font-mono whitespace-pre-wrap text-xs">{code}</div>
546
631
  }
547
632
  const [sidebarOpen, setSidebarOpen] = useState(false)
548
633
  return (
549
- <div className="flex h-screen w-full overflow-hidden">
634
+ <div className="flex h-[98vh] w-full overflow-hidden">
550
635
  <FileSidebar
551
636
  files={Object.fromEntries(files.map((f) => [f.path, f.content]))}
552
637
  currentFile={currentFile}
@@ -579,6 +664,23 @@ export const CodeEditor = ({
579
664
  }
580
665
  />
581
666
  </div>
667
+ {showQuickOpen && (
668
+ <QuickOpen
669
+ files={files.filter((f) => !isHiddenFile(f.path))}
670
+ currentFile={currentFile}
671
+ onFileSelect={handleFileChange}
672
+ onClose={() => setShowQuickOpen(false)}
673
+ />
674
+ )}
675
+ {showGlobalFindReplace && (
676
+ <GlobalFindReplace
677
+ files={files.filter((f) => !isHiddenFile(f.path))}
678
+ currentFile={currentFile}
679
+ onFileSelect={handleFileChange}
680
+ onFileContentChanged={onCodeChange}
681
+ onClose={() => setShowGlobalFindReplace(false)}
682
+ />
683
+ )}
582
684
  </div>
583
685
  )
584
686
  }
@@ -9,7 +9,7 @@ import {
9
9
  DropdownMenuItem,
10
10
  DropdownMenuTrigger,
11
11
  } from "@/components/ui/dropdown-menu"
12
- import { AlertTriangle, PanelRightClose } from "lucide-react"
12
+ import { AlertTriangle, PanelRightClose, Bot } from "lucide-react"
13
13
  import { checkIfManualEditsImported } from "@/lib/utils/checkIfManualEditsImported"
14
14
  import {
15
15
  Select,
@@ -20,6 +20,12 @@ import {
20
20
  } from "../ui/select"
21
21
  import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
22
22
  import { Package } from "fake-snippets-api/lib/db/schema"
23
+ import {
24
+ Tooltip,
25
+ TooltipContent,
26
+ TooltipProvider,
27
+ TooltipTrigger,
28
+ } from "@/components/ui/tooltip"
23
29
 
24
30
  export type FileName = string
25
31
 
@@ -44,6 +50,7 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
44
50
  useImportPackageDialog()
45
51
  const { toast } = useToast()
46
52
  const [sidebarOpen, setSidebarOpen] = fileSidebarState
53
+ const [aiAutocompleteEnabled, setAiAutocompleteEnabled] = useState(false)
47
54
 
48
55
  const handleFormatFile = useCallback(() => {
49
56
  if (!window.prettier || !window.prettierPlugins) return
@@ -152,7 +159,7 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
152
159
  <div>
153
160
  <Select value={currentFile || ""} onValueChange={handleFileChange}>
154
161
  <SelectTrigger
155
- className={`h-7 w-24 px-3 bg-white select-none transition-[margin] duration-300 ease-in-out ${
162
+ className={`h-7 w-32 sm:w-48 px-3 bg-white select-none transition-[margin] duration-300 ease-in-out ${
156
163
  sidebarOpen ? "-ml-2" : "-ml-1"
157
164
  }`}
158
165
  >
@@ -170,7 +177,9 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
170
177
  <SelectItem className="py-1" key={filename} value={filename}>
171
178
  <span
172
179
  className={`text-xs pr-1 block truncate ${
173
- sidebarOpen ? "max-w-[5rem]" : "max-w-[10rem]"
180
+ sidebarOpen
181
+ ? "max-w-[8rem] sm:max-w-[12rem]"
182
+ : "max-w-[12rem] sm:max-w-[16rem]"
174
183
  }`}
175
184
  >
176
185
  {filename}
@@ -187,7 +196,9 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
187
196
  <SelectItem className="select-none py-1" value={currentFile}>
188
197
  <span
189
198
  className={`text-xs pr-1 block truncate ${
190
- sidebarOpen ? "max-w-[5rem]" : "max-w-[10rem]"
199
+ sidebarOpen
200
+ ? "max-w-[8rem] sm:max-w-[12rem]"
201
+ : "max-w-[12rem] sm:max-w-[16rem]"
191
202
  }`}
192
203
  >
193
204
  {currentFile}
@@ -228,6 +239,30 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
228
239
  </DropdownMenuContent>
229
240
  </DropdownMenu>
230
241
  )}
242
+ <TooltipProvider>
243
+ <Tooltip>
244
+ <TooltipTrigger asChild>
245
+ <Button
246
+ size="sm"
247
+ variant="ghost"
248
+ onClick={() =>
249
+ setAiAutocompleteEnabled(!aiAutocompleteEnabled)
250
+ }
251
+ className={`relative bg-transparent ${aiAutocompleteEnabled ? "text-gray-600 bg-gray-50" : "text-gray-400"}`}
252
+ >
253
+ <Bot className="h-4 w-4" />
254
+ {!aiAutocompleteEnabled && (
255
+ <div className="absolute inset-0 flex items-center justify-center">
256
+ <div className="w-5 h-0.5 bg-gray-400 rotate-45 rounded-full" />
257
+ </div>
258
+ )}
259
+ </Button>
260
+ </TooltipTrigger>
261
+ <TooltipContent>
262
+ <p>Toggle AI autocomplete for code suggestions</p>
263
+ </TooltipContent>
264
+ </Tooltip>
265
+ </TooltipProvider>
231
266
  <Button size="sm" variant="ghost" onClick={() => openImportDialog()}>
232
267
  Import
233
268
  </Button>
@@ -10,7 +10,7 @@ import {
10
10
  DropdownMenuTrigger,
11
11
  } from "@/components/ui/dropdown-menu"
12
12
  import { useGlobalStore } from "@/hooks/use-global-store"
13
- import { encodeTextToUrlHash } from "@/lib/encodeTextToUrlHash"
13
+ import { encodeFsMapToUrlHash } from "@/lib/encodeFsMapToUrlHash"
14
14
  import { cn } from "@/lib/utils"
15
15
  import { OpenInNewWindowIcon, LockClosedIcon } from "@radix-ui/react-icons"
16
16
  import { AnyCircuitElement } from "circuit-json"
@@ -32,11 +32,13 @@ import {
32
32
  Sidebar,
33
33
  Sparkles,
34
34
  Trash2,
35
+ Undo2,
35
36
  } from "lucide-react"
36
37
  import { useEffect, useMemo, useState } from "react"
37
38
  import { useQueryClient } from "react-query"
38
39
  import { Link, useLocation } from "wouter"
39
40
  import { useAxios } from "@/hooks/use-axios"
41
+ import { useHotkeyCombo } from "@/hooks/use-hotkey"
40
42
  import { useToast } from "@/hooks/use-toast"
41
43
  import { useConfirmDeletePackageDialog } from "@/components/dialogs/confirm-delete-package-dialog"
42
44
  import { useFilesDialog } from "@/components/dialogs/files-dialog"
@@ -52,22 +54,26 @@ export default function EditorNav({
52
54
  circuitJson,
53
55
  pkg,
54
56
  code,
57
+ fsMap,
55
58
  hasUnsavedChanges,
56
59
  onTogglePreview,
57
60
  previewOpen,
58
61
  onSave,
62
+ onDiscard,
59
63
  packageType,
60
64
  isSaving,
61
65
  }: {
62
66
  pkg?: Package | null
63
67
  circuitJson?: AnyCircuitElement[] | null
64
68
  code: string
69
+ fsMap: Record<string, string>
65
70
  packageType?: string
66
71
  hasUnsavedChanges: boolean
67
72
  previewOpen: boolean
68
73
  onTogglePreview: () => void
69
74
  isSaving: boolean
70
75
  onSave: () => void
76
+ onDiscard?: () => void
71
77
  }) {
72
78
  const [, navigate] = useLocation()
73
79
  const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
@@ -190,17 +196,14 @@ export default function EditorNav({
190
196
  [isLoggedIn, pkg, session?.github_username],
191
197
  )
192
198
 
193
- useEffect(() => {
194
- const handleKeyDown = (e: KeyboardEvent) => {
195
- if ((e.ctrlKey || e.metaKey) && e.key === "s") {
196
- e.preventDefault()
197
- if (!hasUnsavedChanges || !canSavePackage) return
198
- onSave()
199
- }
200
- }
201
- window.addEventListener("keydown", handleKeyDown)
202
- return () => window.removeEventListener("keydown", handleKeyDown)
203
- }, [onSave, hasUnsavedChanges, canSavePackage])
199
+ useHotkeyCombo(
200
+ "cmd+s",
201
+ () => {
202
+ if (!hasUnsavedChanges || !canSavePackage) return
203
+ onSave()
204
+ },
205
+ { target: window },
206
+ )
204
207
  return (
205
208
  <nav className="lg:flex w-screen items-center justify-between px-2 py-3 border-b border-gray-200 bg-white text-sm border-t">
206
209
  <div className="lg:flex items-center my-2 ">
@@ -311,6 +314,17 @@ export default function EditorNav({
311
314
  {pkg ? "unsaved changes" : "unsaved"}
312
315
  </div>
313
316
  )}
317
+ {hasUnsavedChanges && onDiscard && Boolean(pkg?.package_id) && (
318
+ <Button
319
+ variant="ghost"
320
+ size="sm"
321
+ className="h-6 px-2 text-xs text-red-600 hover:text-red-700 hover:bg-red-50"
322
+ onClick={onDiscard}
323
+ title="Discard all unsaved changes (Cmd+Shift+Z)"
324
+ >
325
+ <Undo2 className="mr-1 h-3 w-3" />
326
+ </Button>
327
+ )}
314
328
  </div>
315
329
  </div>
316
330
  <div className="flex items-center justify-end -space-x-1">
@@ -326,16 +340,19 @@ export default function EditorNav({
326
340
  Edit with AI
327
341
  </Button> */}
328
342
  <DownloadButtonAndMenu
329
- snippetUnscopedName={pkg?.unscoped_name}
343
+ offerMultipleImageFormats
344
+ unscopedName={pkg?.unscoped_name}
330
345
  circuitJson={circuitJson}
331
346
  className="flex"
347
+ desiredImageType={pkg?.default_view ?? "pcb"}
348
+ author={pkg?.owner_github_username ?? undefined}
332
349
  />
333
350
  <Button
334
351
  variant="ghost"
335
352
  size="sm"
336
353
  className="hidden md:flex px-2 text-xs"
337
354
  onClick={() => {
338
- const url = encodeTextToUrlHash(code, packageType)
355
+ const url = encodeFsMapToUrlHash(fsMap, packageType)
339
356
  navigator.clipboard.writeText(url)
340
357
  alert("URL copied to clipboard!")
341
358
  }}
@@ -432,7 +449,7 @@ export default function EditorNav({
432
449
  onClick={() => openDeleteDialog()}
433
450
  >
434
451
  <Trash2 className="mr-2 h-3 w-3" />
435
- Delete Snippet
452
+ Delete Package
436
453
  </DropdownMenuItem>
437
454
  <DropdownMenuItem className="text-xs text-gray-500" disabled>
438
455
  @tscircuit/core@{tscircuitCorePkg.version}
@@ -468,6 +485,15 @@ export default function EditorNav({
468
485
  </div>
469
486
  </DropdownMenuTrigger>
470
487
  <DropdownMenuContent>
488
+ {hasUnsavedChanges && onDiscard && (
489
+ <DropdownMenuItem
490
+ className="text-xs text-red-600"
491
+ onClick={onDiscard}
492
+ >
493
+ <Undo2 className="mr-1 h-3 w-3" />
494
+ Discard Changes
495
+ </DropdownMenuItem>
496
+ )}
471
497
  <DropdownMenuItem
472
498
  className="text-xs"
473
499
  onClick={() => {