@tscircuit/fake-snippets 0.0.65 → 0.0.67

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 (87) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/README.md +2 -2
  3. package/bun-tests/fake-snippets-api/fixtures/get-circuit-json.ts +5 -143
  4. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +1 -4
  5. package/bun-tests/fake-snippets-api/fixtures/start-server.ts +7 -3
  6. package/bun-tests/fake-snippets-api/routes/order_quotes/create.test.ts +20 -56
  7. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +2 -2
  8. package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +1 -1
  9. package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +1 -16
  10. package/bun.lock +42 -31
  11. package/dist/bundle.js +34 -41
  12. package/fake-snippets-api/routes/api/order_quotes/create.ts +30 -37
  13. package/fake-snippets-api/routes/api/order_quotes/get.ts +5 -8
  14. package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +3 -3
  15. package/package.json +7 -5
  16. package/src/App.tsx +0 -7
  17. package/src/ContextProviders.tsx +2 -0
  18. package/src/components/DownloadButtonAndMenu.tsx +1 -4
  19. package/src/components/ErrorTabContent.tsx +1 -1
  20. package/src/components/Footer.tsx +5 -2
  21. package/src/components/HeaderLogin.tsx +37 -54
  22. package/src/components/ImageWithFallback.tsx +37 -0
  23. package/src/components/JLCPCBImportDialog.tsx +43 -24
  24. package/src/components/PackageCard.tsx +12 -3
  25. package/src/components/{SnippetLink.tsx → PackageLink.tsx} +8 -16
  26. package/src/components/PackageSearchResults.tsx +87 -0
  27. package/src/components/PackagesList.tsx +3 -3
  28. package/src/components/PageSearchComponent.tsx +9 -9
  29. package/src/components/ShippingInformationForm.tsx +1 -1
  30. package/src/components/TrendingPackagesCarousel.tsx +43 -23
  31. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +5 -28
  32. package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -22
  33. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +25 -14
  34. package/src/components/ViewPackagePage/components/package-header.tsx +9 -4
  35. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +6 -1
  36. package/src/components/ViewPackagePage/components/repo-page-content.tsx +4 -4
  37. package/src/components/ViewPackagePage/components/sidebar.tsx +2 -14
  38. package/src/components/ViewSnippetSidebar.tsx +1 -1
  39. package/src/components/package-port/CodeEditor.tsx +13 -10
  40. package/src/components/package-port/CodeEditorHeader.tsx +1 -1
  41. package/src/components/package-port/EditorNav.tsx +2 -2
  42. package/src/hooks/use-get-fsmap-hash-for-package.ts +19 -0
  43. package/src/hooks/use-global-store.ts +1 -0
  44. package/src/hooks/use-preview-images.ts +20 -4
  45. package/src/hooks/use-shiki-highlighter.ts +13 -6
  46. package/src/hooks/use-toast.tsx +1 -1
  47. package/src/lib/download-fns/download-gltf.ts +3 -10
  48. package/src/lib/handleManualEditsImport.tsx +1 -1
  49. package/src/lib/types.ts +4 -2
  50. package/src/pages/dashboard.tsx +3 -4
  51. package/src/pages/editor.tsx +20 -14
  52. package/src/pages/latest.tsx +25 -26
  53. package/src/pages/package-editor.tsx +14 -2
  54. package/src/pages/search.tsx +120 -19
  55. package/src/pages/trending.tsx +14 -59
  56. package/src/pages/user-profile.tsx +13 -8
  57. package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +0 -84
  58. package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +0 -53
  59. package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +0 -82
  60. package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +0 -90
  61. package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +0 -16
  62. package/bun-tests/fake-snippets-api/routes/snippets/get.test.ts +0 -163
  63. package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +0 -117
  64. package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +0 -114
  65. package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +0 -169
  66. package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +0 -50
  67. package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +0 -72
  68. package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +0 -80
  69. package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +0 -75
  70. package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +0 -51
  71. package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +0 -175
  72. package/src/components/AiChatInterface.tsx +0 -229
  73. package/src/components/CodeAndPreview.tsx +0 -289
  74. package/src/components/CodeEditor.tsx +0 -539
  75. package/src/components/CodeEditorHeader.tsx +0 -135
  76. package/src/components/EditorNav.tsx +0 -502
  77. package/src/components/PreviewContent.tsx +0 -372
  78. package/src/components/SnippetCard.tsx +0 -159
  79. package/src/components/SnippetList.tsx +0 -71
  80. package/src/hooks/use-compiled-tsx.ts +0 -37
  81. package/src/hooks/use-run-tsx/construct-circuit.tsx +0 -62
  82. package/src/hooks/use-run-tsx/index.tsx +0 -256
  83. package/src/hooks/use-save-snippet.ts +0 -66
  84. package/src/hooks/use-typecheck.ts +0 -54
  85. package/src/lib/utils/getSyntaxError.ts +0 -13
  86. package/src/pages/ai.tsx +0 -92
  87. package/src/pages/view-snippet.tsx +0 -166
@@ -1,372 +0,0 @@
1
- import { CodeEditor } from "@/components/CodeEditor"
2
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
3
- import { cn } from "@/lib/utils"
4
- import { applyPcbEditEvents } from "@/lib/utils/pcbManualEditEventHandler"
5
- import { CadViewer } from "@tscircuit/3d-viewer"
6
- // import { PCBViewer } from "@tscircuit/pcb-viewer"
7
- // import { SchematicViewer } from "@tscircuit/schematic-viewer"
8
- import { useEffect, useRef, useState } from "react"
9
- import { ErrorFallback } from "./ErrorFallback"
10
- import { ErrorBoundary } from "react-error-boundary"
11
- import { ErrorTabContent } from "./ErrorTabContent"
12
- import PreviewEmptyState from "./PreviewEmptyState"
13
- import { RunButton } from "./RunButton"
14
- import { CircuitJsonTableViewer } from "./TableViewer/CircuitJsonTableViewer"
15
- import { CircuitToSvgWithMouseControl } from "./CircuitToSvgWithMouseControl"
16
- import { BomTable } from "./BomTable"
17
- import {
18
- CheckIcon,
19
- EllipsisIcon,
20
- EllipsisVerticalIcon,
21
- FullscreenIcon,
22
- MinimizeIcon,
23
- } from "lucide-react"
24
- import {
25
- DropdownMenu,
26
- DropdownMenuContent,
27
- DropdownMenuItem,
28
- DropdownMenuTrigger,
29
- } from "./ui/dropdown-menu"
30
- import { Button } from "./ui/button"
31
- import { PcbViewerWithContainerHeight } from "./PcbViewerWithContainerHeight"
32
- import type { EditEvent } from "@tscircuit/manual-edit-events"
33
-
34
- export interface PreviewContentProps {
35
- code: string
36
- readOnly?: boolean
37
- triggerRunTsx: () => void
38
- tsxRunTriggerCount: number
39
- errorMessage: string | null
40
- circuitJson: any
41
- circuitJsonKey?: string
42
- className?: string
43
- showCodeTab?: boolean
44
- showJsonTab?: boolean
45
- showImportAndFormatButtons?: boolean
46
- headerClassName?: string
47
- leftHeaderContent?: React.ReactNode
48
- isRunningCode?: boolean
49
- isStreaming?: boolean
50
- onToggleFullScreen?: () => void
51
- isFullScreen?: boolean
52
- onCodeChange?: (code: string) => void
53
- onDtsChange?: (dts: string) => void
54
- manualEditsFileContent?: string
55
- onManualEditsFileContentChange?: (newmanualEditsFileContent: string) => void
56
- }
57
-
58
- export const PreviewContent = ({
59
- code,
60
- triggerRunTsx,
61
- tsxRunTriggerCount,
62
- errorMessage,
63
- circuitJsonKey = "",
64
- circuitJson,
65
- showCodeTab = false,
66
- showJsonTab = true,
67
- showImportAndFormatButtons = true,
68
- className,
69
- headerClassName,
70
- leftHeaderContent,
71
- readOnly,
72
- isStreaming,
73
- onToggleFullScreen,
74
- isFullScreen,
75
- isRunningCode,
76
- onCodeChange,
77
- onDtsChange,
78
- manualEditsFileContent,
79
- onManualEditsFileContentChange,
80
- }: PreviewContentProps) => {
81
- const [activeTab, setActiveTab] = useState(showCodeTab ? "code" : "pcb")
82
- const [lastRunHash, setLastRunHash] = useState("")
83
- const threeJsObjectRef = useRef<any>(null)
84
-
85
- useEffect(() => {
86
- window.TSCIRCUIT_3D_OBJECT_REF = threeJsObjectRef
87
- }, [])
88
-
89
- const currentCodeHash = code + "\n" + manualEditsFileContent
90
- const hasCodeChangedSinceLastRun = lastRunHash !== currentCodeHash
91
-
92
- useEffect(() => {
93
- if (tsxRunTriggerCount === 0) return
94
- setLastRunHash(currentCodeHash)
95
- }, [tsxRunTriggerCount])
96
-
97
- useEffect(() => {
98
- if (errorMessage) {
99
- setActiveTab("error")
100
- }
101
- }, [errorMessage])
102
-
103
- useEffect(() => {
104
- if (activeTab === "code" && circuitJson && !errorMessage) {
105
- setActiveTab("pcb")
106
- }
107
- }, [circuitJson])
108
-
109
- return (
110
- <div className={cn("flex flex-col relative", className)}>
111
- <div className="md:sticky md:top-2">
112
- <Tabs
113
- value={activeTab}
114
- onValueChange={setActiveTab}
115
- className="flex-grow flex flex-col"
116
- >
117
- <div className={cn("flex items-center gap-2", headerClassName)}>
118
- {leftHeaderContent}
119
- {leftHeaderContent && <div className="flex-grow" />}
120
- <RunButton
121
- onClick={() => triggerRunTsx()}
122
- disabled={!hasCodeChangedSinceLastRun && tsxRunTriggerCount !== 0}
123
- isRunningCode={isRunningCode}
124
- />
125
- {!leftHeaderContent && <div className="flex-grow" />}
126
- <TabsList>
127
- {showCodeTab && <TabsTrigger value="code">Code</TabsTrigger>}
128
- <TabsTrigger value="pcb" className="whitespace-nowrap">
129
- {circuitJson && (
130
- <span
131
- className={cn(
132
- "inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
133
- !hasCodeChangedSinceLastRun
134
- ? "bg-blue-500"
135
- : "bg-gray-500",
136
- )}
137
- />
138
- )}
139
- PCB
140
- </TabsTrigger>
141
- <TabsTrigger value="schematic" className="whitespace-nowrap">
142
- {circuitJson && (
143
- <span
144
- className={cn(
145
- "inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
146
- !hasCodeChangedSinceLastRun
147
- ? "bg-blue-500"
148
- : "bg-gray-500",
149
- )}
150
- />
151
- )}
152
- Schematic
153
- </TabsTrigger>
154
- <TabsTrigger value="cad">
155
- {circuitJson && (
156
- <span
157
- className={cn(
158
- "inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
159
- !hasCodeChangedSinceLastRun
160
- ? "bg-blue-500"
161
- : "bg-gray-500",
162
- )}
163
- />
164
- )}
165
- 3D
166
- </TabsTrigger>
167
- <DropdownMenu>
168
- <DropdownMenuTrigger asChild>
169
- <div className="whitespace-nowrap p-2 mr-1 cursor-pointer relative">
170
- <EllipsisIcon className="w-4 h-4" />
171
- {errorMessage && (
172
- <span className="inline-flex absolute top-[6px] right-[4px] items-center justify-center w-1 h-1 ml-2 text-[8px] font-bold text-white bg-red-500 rounded-full" />
173
- )}
174
- </div>
175
- </DropdownMenuTrigger>
176
- <DropdownMenuContent className="*:text-xs">
177
- <DropdownMenuItem
178
- onSelect={() => setActiveTab("error")}
179
- className="flex"
180
- >
181
- <CheckIcon
182
- className={cn(
183
- "w-3 h-3 mr-2",
184
- activeTab !== "error" && "invisible",
185
- )}
186
- />
187
- <div className="flex-grow">Errors</div>
188
- {errorMessage && (
189
- <span className="inline-flex items-center justify-center w-3 h-3 ml-2 text-[8px] font-bold text-white bg-red-500 rounded-full">
190
- 1
191
- </span>
192
- )}
193
- </DropdownMenuItem>
194
- <DropdownMenuItem onSelect={() => setActiveTab("bom")}>
195
- <CheckIcon
196
- className={cn(
197
- "w-3 h-3 mr-2",
198
- activeTab !== "bom" && "invisible",
199
- )}
200
- />
201
- Bill of Materials
202
- </DropdownMenuItem>
203
- <DropdownMenuItem
204
- onSelect={() => setActiveTab("circuitjson")}
205
- >
206
- <CheckIcon
207
- className={cn(
208
- "w-3 h-3 mr-2",
209
- activeTab !== "circuitjson" && "invisible",
210
- )}
211
- />
212
- JSON
213
- </DropdownMenuItem>
214
- </DropdownMenuContent>
215
- </DropdownMenu>
216
- </TabsList>
217
- {onToggleFullScreen && (
218
- <Button onClick={onToggleFullScreen} variant="ghost">
219
- {isFullScreen ? (
220
- <MinimizeIcon size={16} />
221
- ) : (
222
- <FullscreenIcon size={16} />
223
- )}
224
- </Button>
225
- )}
226
- </div>
227
- {showCodeTab && (
228
- <TabsContent value="code" className="flex-grow overflow-hidden">
229
- <div className="h-full">
230
- <CodeEditor
231
- initialCode={code}
232
- manualEditsFileContent={manualEditsFileContent ?? ""}
233
- isStreaming={isStreaming}
234
- onCodeChange={onCodeChange!}
235
- onDtsChange={onDtsChange!}
236
- readOnly={readOnly}
237
- showImportAndFormatButtons={showImportAndFormatButtons}
238
- />
239
- </div>
240
- </TabsContent>
241
- )}
242
-
243
- <TabsContent value="pcb">
244
- <div
245
- className={cn(
246
- "mt-4 overflow-hidden",
247
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
248
- )}
249
- >
250
- <ErrorBoundary fallback={<div>Error loading PCB viewer</div>}>
251
- {circuitJson ? (
252
- <PcbViewerWithContainerHeight
253
- key={circuitJsonKey}
254
- circuitJson={circuitJson}
255
- containerClassName={cn(
256
- "h-full w-full",
257
- isFullScreen
258
- ? "min-h-[calc(100vh-240px)]"
259
- : "min-h-[620px]",
260
- )}
261
- onEditEventsChanged={(editEvents) => {
262
- if (editEvents.some((editEvent) => editEvent.in_progress))
263
- return
264
- // Update state with new edit events
265
- const newManualEditsFileContent = applyPcbEditEvents({
266
- editEvents: editEvents as EditEvent[],
267
- circuitJson,
268
- manualEditsFileContent,
269
- })
270
- onManualEditsFileContentChange?.(
271
- JSON.stringify(newManualEditsFileContent, null, 2),
272
- )
273
- }}
274
- />
275
- ) : (
276
- <PreviewEmptyState triggerRunTsx={triggerRunTsx} />
277
- )}
278
- </ErrorBoundary>
279
- </div>
280
- </TabsContent>
281
-
282
- <TabsContent value="schematic">
283
- <div
284
- className={cn(
285
- "mt-4 overflow-auto",
286
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
287
- )}
288
- >
289
- <ErrorBoundary fallback={<div>Error loading Schematic</div>}>
290
- {circuitJson ? (
291
- <CircuitToSvgWithMouseControl
292
- key={tsxRunTriggerCount}
293
- circuitJson={circuitJson}
294
- />
295
- // Waiting for Schematic Viewer to stablize
296
- // <Schematic
297
- // style={{ height: "500px" }}
298
- // key={tsxRunTriggerCount}
299
- // soup={circuitJson}
300
- // />
301
- ) : (
302
- <PreviewEmptyState triggerRunTsx={triggerRunTsx} />
303
- )}
304
- </ErrorBoundary>
305
- </div>
306
- </TabsContent>
307
-
308
- <TabsContent value="cad">
309
- <div
310
- className={cn(
311
- "mt-4 overflow-auto",
312
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
313
- )}
314
- >
315
- <ErrorBoundary FallbackComponent={ErrorFallback}>
316
- {circuitJson ? (
317
- <CadViewer
318
- circuitJson={circuitJson as any}
319
- ref={threeJsObjectRef}
320
- />
321
- ) : (
322
- <PreviewEmptyState triggerRunTsx={triggerRunTsx} />
323
- )}
324
- </ErrorBoundary>
325
- </div>
326
- </TabsContent>
327
-
328
- <TabsContent value="bom">
329
- <div
330
- className={cn(
331
- "mt-4 overflow-auto",
332
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
333
- )}
334
- >
335
- <ErrorBoundary fallback={<div>Error loading BOM</div>}>
336
- {circuitJson ? (
337
- <BomTable circuitJson={circuitJson} />
338
- ) : (
339
- <PreviewEmptyState triggerRunTsx={triggerRunTsx} />
340
- )}
341
- </ErrorBoundary>
342
- </div>
343
- </TabsContent>
344
-
345
- <TabsContent value="circuitjson">
346
- <div
347
- className={cn(
348
- "mt-4 overflow-auto",
349
- isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
350
- )}
351
- >
352
- <ErrorBoundary fallback={<div>Error loading JSON viewer</div>}>
353
- {circuitJson ? (
354
- <CircuitJsonTableViewer elements={circuitJson as any} />
355
- ) : (
356
- <PreviewEmptyState triggerRunTsx={triggerRunTsx} />
357
- )}
358
- </ErrorBoundary>
359
- </div>
360
- </TabsContent>
361
- <TabsContent value="error">
362
- {circuitJson || errorMessage ? (
363
- <ErrorTabContent code={code} errorMessage={errorMessage} />
364
- ) : (
365
- <PreviewEmptyState triggerRunTsx={triggerRunTsx} />
366
- )}
367
- </TabsContent>
368
- </Tabs>
369
- </div>
370
- </div>
371
- )
372
- }
@@ -1,159 +0,0 @@
1
- import React from "react"
2
- import { Link } from "wouter"
3
- import { Snippet } from "fake-snippets-api/lib/db/schema"
4
- import { GitHubLogoIcon, StarIcon, LockClosedIcon } from "@radix-ui/react-icons"
5
- import { GlobeIcon, MoreVertical, PencilIcon, Trash2 } from "lucide-react"
6
- import { Button } from "@/components/ui/button"
7
- import {
8
- DropdownMenu,
9
- DropdownMenuContent,
10
- DropdownMenuItem,
11
- DropdownMenuTrigger,
12
- } from "@/components/ui/dropdown-menu"
13
- import { OptimizedImage } from "./OptimizedImage"
14
- import { SnippetTypeIcon } from "./SnippetTypeIcon"
15
- import { timeAgo } from "@/lib/utils/timeAgo"
16
-
17
- export interface SnippetCardProps {
18
- /** The snippet data to display */
19
- snippet: Snippet
20
- /** Base URL for snippet images */
21
- baseUrl: string
22
- /** Whether to show the owner name (useful in starred views) */
23
- showOwner?: boolean
24
- /** Whether this is the current user's snippet (enables edit/delete options) */
25
- isCurrentUserSnippet?: boolean
26
- /** Callback when delete is clicked */
27
- onDeleteClick?: (e: React.MouseEvent, snippet: Snippet) => void
28
- /** Custom class name for the card container */
29
- className?: string
30
- /** Custom image size (default is h-16 w-16) */
31
- imageSize?: string
32
- /** Custom image transform style */
33
- imageTransform?: string
34
- /** Whether to render the card with a link to the snippet page */
35
- withLink?: boolean
36
- /** Custom render function for actions */
37
- renderActions?: (snippet: Snippet) => React.ReactNode
38
- }
39
-
40
- export const SnippetCard: React.FC<SnippetCardProps> = ({
41
- snippet,
42
- baseUrl,
43
- showOwner = false,
44
- isCurrentUserSnippet = false,
45
- onDeleteClick,
46
- className = "",
47
- imageSize = "h-16 w-16",
48
- imageTransform = "transition-transform duration-300 -rotate-45 hover:rotate-0 hover:scale-110 scale-150",
49
- withLink = true,
50
- renderActions,
51
- }) => {
52
- const handleDeleteClick = (e: React.MouseEvent) => {
53
- e.preventDefault() // Prevent navigation
54
- if (onDeleteClick) {
55
- onDeleteClick(e, snippet)
56
- }
57
- }
58
-
59
- const cardContent = (
60
- <div
61
- className={`border p-4 rounded-md hover:shadow-md transition-shadow flex flex-col gap-4 ${className}`}
62
- >
63
- <div className="flex items-start gap-4">
64
- <div
65
- className={`${imageSize} flex-shrink-0 rounded-md overflow-hidden`}
66
- >
67
- <OptimizedImage
68
- src={`${baseUrl}/snippets/images/${snippet.owner_name}/${snippet.unscoped_name}/pcb.svg`}
69
- alt={`${snippet.owner_name}'s profile`}
70
- className={`object-cover h-full w-full ${imageTransform}`}
71
- />
72
- </div>
73
- <div className="flex-1 min-w-0">
74
- <div className="flex justify-between items-start mb-[2px] -mt-[3px]">
75
- <h2 className="text-md font-semibold truncate pr-[30px]">
76
- {showOwner && (
77
- <>
78
- <span className="text-gray-700 text-md">
79
- {snippet.owner_name}
80
- </span>
81
- <span className="mx-1">/</span>
82
- </>
83
- )}
84
- <span className="text-gray-900">{snippet.unscoped_name}</span>
85
- </h2>
86
- <div className="flex items-center gap-2">
87
- <SnippetTypeIcon
88
- type={snippet.snippet_type}
89
- className="pt-[2.5px]"
90
- />
91
- <div className="flex items-center gap-1 text-gray-600">
92
- <StarIcon className="w-4 h-4 pt-[2.5px]" />
93
- <span className="text-[16px]">{snippet.star_count || 0}</span>
94
- </div>
95
- {isCurrentUserSnippet && onDeleteClick && (
96
- <DropdownMenu>
97
- <DropdownMenuTrigger asChild>
98
- <Button
99
- variant="ghost"
100
- size="icon"
101
- className="h-[1.5rem] w-[1.5rem]"
102
- >
103
- <MoreVertical className="h-4 w-4" />
104
- </Button>
105
- </DropdownMenuTrigger>
106
- <DropdownMenuContent>
107
- <DropdownMenuItem
108
- className="text-xs text-red-600"
109
- onClick={handleDeleteClick}
110
- >
111
- <Trash2 className="mr-2 h-3 w-3" />
112
- Delete Snippet
113
- </DropdownMenuItem>
114
- </DropdownMenuContent>
115
- </DropdownMenu>
116
- )}
117
- {renderActions && renderActions(snippet)}
118
- </div>
119
- </div>
120
- <p
121
- className={`${!snippet.description && "h-[1.25rem]"} text-sm text-gray-500 mb-1 truncate max-w-xs`}
122
- >
123
- {snippet.description ? snippet.description : " "}
124
- </p>
125
- <div className={`flex items-center gap-4`}>
126
- {snippet.is_private ? (
127
- <div className="flex items-center text-xs gap-1 text-gray-500">
128
- <LockClosedIcon height={12} width={12} />
129
- <span>Private</span>
130
- </div>
131
- ) : (
132
- <div className="flex items-center text-xs gap-1 text-gray-500">
133
- <GlobeIcon height={12} width={12} />
134
- <span>Public</span>
135
- </div>
136
- )}
137
- <div className="flex items-center text-xs gap-1 text-gray-500">
138
- <PencilIcon height={12} width={12} />
139
- <span>{timeAgo(new Date(snippet.updated_at))}</span>
140
- </div>
141
- </div>
142
- </div>
143
- </div>
144
- </div>
145
- )
146
-
147
- if (withLink) {
148
- return (
149
- <Link
150
- key={snippet.snippet_id}
151
- href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
152
- >
153
- {cardContent}
154
- </Link>
155
- )
156
- }
157
-
158
- return cardContent
159
- }
@@ -1,71 +0,0 @@
1
- import { Button } from "@/components/ui/button"
2
- import { ChevronDown, ChevronUp, Star } from "lucide-react"
3
- import { Link } from "wouter"
4
- import { Snippet } from "fake-snippets-api/lib/db/schema"
5
-
6
- interface SnippetListProps {
7
- title: string
8
- snippets?: Snippet[]
9
- showAll?: boolean
10
- onToggleShowAll?: () => void
11
- maxItems?: number
12
- }
13
-
14
- export const SnippetList = ({
15
- title,
16
- snippets = [],
17
- showAll = false,
18
- onToggleShowAll,
19
- maxItems = 5,
20
- }: SnippetListProps) => {
21
- const displayedSnippets = showAll ? snippets : snippets.slice(0, maxItems)
22
-
23
- return (
24
- <div>
25
- <div className="flex items-center justify-between">
26
- <h2 className="text-sm font-bold text-gray-700">{title}</h2>
27
- {snippets.length > maxItems && onToggleShowAll && (
28
- <Button
29
- variant="ghost"
30
- size="sm"
31
- onClick={onToggleShowAll}
32
- className="text-blue-600 hover:text-blue-700 hover:bg-transparent"
33
- >
34
- {showAll ? (
35
- <>
36
- Show less <ChevronUp className="w-3 h-3 ml-1" />
37
- </>
38
- ) : (
39
- <>
40
- Show more <ChevronDown className="w-3 h-3 ml-1" />
41
- </>
42
- )}
43
- </Button>
44
- )}
45
- </div>
46
- <div className="border-b border-gray-200" />
47
- {snippets && (
48
- <ul className="space-y-1 mt-2">
49
- {displayedSnippets.map((snippet) => (
50
- <li key={snippet.snippet_id}>
51
- <div className="flex items-center">
52
- <Link
53
- href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
54
- className="text-blue-600 hover:underline text-sm"
55
- >
56
- {snippet.owner_name}/{snippet.unscoped_name}
57
- </Link>
58
- {snippet.star_count > 0 && (
59
- <span className="ml-2 text-gray-500 text-xs flex items-center">
60
- <Star className="w-3 h-3 mr-1" />
61
- {snippet.star_count}
62
- </span>
63
- )}
64
- </div>
65
- </li>
66
- ))}
67
- </ul>
68
- )}
69
- </div>
70
- )
71
- }
@@ -1,37 +0,0 @@
1
- import { useMemo } from "react"
2
- import * as Babel from "@babel/standalone"
3
-
4
- export const safeCompileTsx = (
5
- code: string,
6
- ):
7
- | { success: true; compiledTsx: string; error?: undefined }
8
- | { success: false; error: Error; compiledTsx?: undefined } => {
9
- try {
10
- return {
11
- success: true,
12
- compiledTsx:
13
- Babel.transform(code, {
14
- presets: ["react", "typescript"],
15
- plugins: ["transform-modules-commonjs"],
16
- filename: "virtual.tsx",
17
- }).code || "",
18
- }
19
- } catch (error: any) {
20
- return { success: false, error }
21
- }
22
- }
23
-
24
- export const useCompiledTsx = (
25
- code?: string,
26
- { isStreaming = false }: { isStreaming?: boolean } = {},
27
- ) => {
28
- return useMemo(() => {
29
- if (!code) return ""
30
- if (isStreaming) return ""
31
- const result = safeCompileTsx(code)
32
- if (result.success) {
33
- return result.compiledTsx
34
- }
35
- return `Error: ${result.error.message}`
36
- }, [code, isStreaming])
37
- }