@tscircuit/fake-snippets 0.0.6 → 0.0.7
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.
- package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +375 -0
- package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +248 -0
- package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +220 -0
- package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +204 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +0 -1
- package/bun.lock +97 -73
- package/dist/bundle.js +584 -212
- package/fake-snippets-api/lib/db/db-client.ts +13 -0
- package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +168 -0
- package/fake-snippets-api/lib/package_release/find-package-release-id.ts +122 -0
- package/fake-snippets-api/routes/api/package_files/create.ts +132 -0
- package/fake-snippets-api/routes/api/package_files/download.ts +70 -153
- package/fake-snippets-api/routes/api/package_files/get.ts +24 -5
- package/fake-snippets-api/routes/api/package_files/list.ts +16 -28
- package/index.html +12 -1
- package/package.json +9 -9
- package/playwright-tests/profile-page.spec.ts +59 -0
- package/playwright-tests/snapshots/profile-page.spec.ts-profile-page-before-delete.png +0 -0
- package/playwright-tests/snapshots/profile-page.spec.ts-profile-page-delete-dialog.png +0 -0
- package/playwright-tests/snapshots/profile-page.spec.ts-profile-page-dropdown-open.png +0 -0
- package/scripts/generate-image-sizes.ts +0 -1
- package/scripts/generate_bundle_stats.js +22 -6
- package/src/components/AiChatInterface.tsx +8 -0
- package/src/components/Analytics.tsx +1 -1
- package/src/components/CodeAndPreview.tsx +9 -3
- package/src/components/CreateNewSnippetWithAiHero.tsx +6 -2
- package/src/components/EditorNav.tsx +4 -0
- package/src/components/Footer.tsx +1 -1
- package/src/components/Header.tsx +7 -10
- package/src/components/HeaderLogin.tsx +1 -1
- package/src/components/PreviewContent.tsx +4 -1
- package/src/components/SnippetList.tsx +71 -0
- package/src/lib/templates/blinking-led-board-template.ts +2 -1
- package/src/pages/dashboard.tsx +19 -44
- package/src/pages/editor.tsx +1 -1
- package/src/pages/landing.tsx +8 -16
- package/src/pages/quickstart.tsx +9 -9
- package/src/pages/user-profile.tsx +50 -3
package/src/pages/dashboard.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react"
|
|
1
|
+
import React, { useState } from "react"
|
|
2
2
|
import { useQuery } from "react-query"
|
|
3
3
|
import { useAxios } from "@/hooks/use-axios"
|
|
4
4
|
import Header from "@/components/Header"
|
|
@@ -6,15 +6,18 @@ import Footer from "@/components/Footer"
|
|
|
6
6
|
import { Snippet } from "fake-snippets-api/lib/db/schema"
|
|
7
7
|
import { Link } from "wouter"
|
|
8
8
|
import { CreateNewSnippetWithAiHero } from "@/components/CreateNewSnippetWithAiHero"
|
|
9
|
-
import { Edit2, Star } from "lucide-react"
|
|
9
|
+
import { Edit2, Star, ChevronDown, ChevronUp } from "lucide-react"
|
|
10
10
|
import { Button } from "@/components/ui/button"
|
|
11
11
|
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
12
12
|
import { PrefetchPageLink } from "@/components/PrefetchPageLink"
|
|
13
|
+
import { SnippetList } from "@/components/SnippetList"
|
|
13
14
|
|
|
14
15
|
export const DashboardPage = () => {
|
|
15
16
|
const axios = useAxios()
|
|
16
17
|
|
|
17
18
|
const currentUser = useGlobalStore((s) => s.session?.github_username)
|
|
19
|
+
const [showAllTrending, setShowAllTrending] = useState(false)
|
|
20
|
+
const [showAllNewest, setShowAllNewest] = useState(false)
|
|
18
21
|
|
|
19
22
|
const {
|
|
20
23
|
data: mySnippets,
|
|
@@ -114,48 +117,20 @@ export const DashboardPage = () => {
|
|
|
114
117
|
)}
|
|
115
118
|
</div>
|
|
116
119
|
<div className="md:w-1/4">
|
|
117
|
-
<
|
|
118
|
-
Trending Snippets
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
{snippet.star_count > 0 && (
|
|
132
|
-
<span className="ml-2 text-gray-500 text-xs flex items-center">
|
|
133
|
-
<Star className="w-3 h-3 mr-1" />
|
|
134
|
-
{snippet.star_count}
|
|
135
|
-
</span>
|
|
136
|
-
)}
|
|
137
|
-
</div>
|
|
138
|
-
</li>
|
|
139
|
-
))}
|
|
140
|
-
</ul>
|
|
141
|
-
)}
|
|
142
|
-
<h2 className="text-sm font-bold mt-8 mb-2 text-gray-700 border-b border-gray-200">
|
|
143
|
-
Newest Snippets
|
|
144
|
-
</h2>
|
|
145
|
-
{newestSnippets && (
|
|
146
|
-
<ul className="space-y-1">
|
|
147
|
-
{newestSnippets.map((snippet) => (
|
|
148
|
-
<li key={snippet.snippet_id}>
|
|
149
|
-
<Link
|
|
150
|
-
href={`/${snippet.name}`}
|
|
151
|
-
className="text-blue-600 hover:underline text-sm"
|
|
152
|
-
>
|
|
153
|
-
{snippet.name}
|
|
154
|
-
</Link>
|
|
155
|
-
</li>
|
|
156
|
-
))}
|
|
157
|
-
</ul>
|
|
158
|
-
)}
|
|
120
|
+
<SnippetList
|
|
121
|
+
title="Trending Snippets"
|
|
122
|
+
snippets={trendingSnippets}
|
|
123
|
+
showAll={showAllTrending}
|
|
124
|
+
onToggleShowAll={() => setShowAllTrending(!showAllTrending)}
|
|
125
|
+
/>
|
|
126
|
+
<div className="mt-8">
|
|
127
|
+
<SnippetList
|
|
128
|
+
title="Newest Snippets"
|
|
129
|
+
snippets={newestSnippets}
|
|
130
|
+
showAll={showAllNewest}
|
|
131
|
+
onToggleShowAll={() => setShowAllNewest(!showAllNewest)}
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
159
134
|
</div>
|
|
160
135
|
</div>
|
|
161
136
|
</div>
|
package/src/pages/editor.tsx
CHANGED
|
@@ -9,7 +9,7 @@ export const EditorPage = () => {
|
|
|
9
9
|
const { data: snippet, isLoading, error } = useSnippet(snippetId)
|
|
10
10
|
|
|
11
11
|
return (
|
|
12
|
-
<div>
|
|
12
|
+
<div className="overflow-x-hidden">
|
|
13
13
|
<Header />
|
|
14
14
|
{!error && <CodeAndPreview snippet={snippet} />}
|
|
15
15
|
{error && error.status === 404 && (
|
package/src/pages/landing.tsx
CHANGED
|
@@ -50,8 +50,8 @@ export function LandingPage() {
|
|
|
50
50
|
<Badge variant="secondary" className="w-fit">
|
|
51
51
|
Open-Source, MIT Licensed
|
|
52
52
|
</Badge>
|
|
53
|
-
<h1 className="text-3xl font-bold tracking-
|
|
54
|
-
|
|
53
|
+
<h1 className="text-3xl font-bold tracking-tight sm:text-5xl xl:text-6xl/none">
|
|
54
|
+
AI codes electronics with tscircuit
|
|
55
55
|
</h1>
|
|
56
56
|
<p className="max-w-[600px] text-muted-foreground md:text-xl">
|
|
57
57
|
Build electronics with code, AI, and drag'n'drop tools.
|
|
@@ -60,20 +60,12 @@ export function LandingPage() {
|
|
|
60
60
|
and more.
|
|
61
61
|
</p>
|
|
62
62
|
</div>
|
|
63
|
-
<div className="flex flex-col items-center gap-2 min-[
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
navigate("/dashboard")
|
|
70
|
-
}
|
|
71
|
-
}}
|
|
72
|
-
size="lg"
|
|
73
|
-
aria-label="Get started with TSCircuit"
|
|
74
|
-
>
|
|
75
|
-
Get Started
|
|
76
|
-
</Button>
|
|
63
|
+
<div className="flex flex-col items-center gap-2 min-[500px]:flex-row">
|
|
64
|
+
<a href="https://docs.tscircuit.com">
|
|
65
|
+
<Button size="lg" aria-label="Get started with TSCircuit">
|
|
66
|
+
Get Started
|
|
67
|
+
</Button>
|
|
68
|
+
</a>
|
|
77
69
|
<PrefetchPageLink href="/quickstart">
|
|
78
70
|
<Button
|
|
79
71
|
size="lg"
|
package/src/pages/quickstart.tsx
CHANGED
|
@@ -65,13 +65,13 @@ export const QuickstartPage = () => {
|
|
|
65
65
|
key={snippet.snippet_id}
|
|
66
66
|
href={`/editor?snippet_id=${snippet.snippet_id}`}
|
|
67
67
|
>
|
|
68
|
-
<Card className="hover:shadow-md transition-shadow rounded-md">
|
|
68
|
+
<Card className="hover:shadow-md transition-shadow rounded-md flex flex-col h-full">
|
|
69
69
|
<CardHeader className="pb-0 p-4">
|
|
70
70
|
<CardTitle className="text-md">
|
|
71
71
|
{snippet.unscoped_name}
|
|
72
72
|
</CardTitle>
|
|
73
73
|
</CardHeader>
|
|
74
|
-
<CardContent className="p-4 pt-0">
|
|
74
|
+
<CardContent className="p-4 pt-0 mt-auto">
|
|
75
75
|
<p className="text-sm text-gray-500">
|
|
76
76
|
Last edited:{" "}
|
|
77
77
|
{new Date(snippet.updated_at).toLocaleDateString()}
|
|
@@ -118,7 +118,7 @@ export const QuickstartPage = () => {
|
|
|
118
118
|
|
|
119
119
|
<div className="mt-12">
|
|
120
120
|
<h2 className="text-xl font-semibold mb-4">Import as Snippet</h2>
|
|
121
|
-
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
121
|
+
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
122
122
|
{[
|
|
123
123
|
{ name: "KiCad Footprint", type: "footprint" },
|
|
124
124
|
{ name: "KiCad Project", type: "board" },
|
|
@@ -126,7 +126,7 @@ export const QuickstartPage = () => {
|
|
|
126
126
|
].map((template, index) => (
|
|
127
127
|
<Card
|
|
128
128
|
key={index}
|
|
129
|
-
className="hover:shadow-md transition-shadow rounded-md opacity-50"
|
|
129
|
+
className="hover:shadow-md transition-shadow rounded-md opacity-50 flex flex-col"
|
|
130
130
|
>
|
|
131
131
|
<CardHeader className="p-4 pb-0">
|
|
132
132
|
<CardTitle className="text-lg flex items-center justify-between">
|
|
@@ -134,7 +134,7 @@ export const QuickstartPage = () => {
|
|
|
134
134
|
<TypeBadge type={template.type as any} className="ml-2" />
|
|
135
135
|
</CardTitle>
|
|
136
136
|
</CardHeader>
|
|
137
|
-
<CardContent className="p-4">
|
|
137
|
+
<CardContent className="p-4 mt-auto">
|
|
138
138
|
<Button
|
|
139
139
|
className="w-full"
|
|
140
140
|
onClick={() => {
|
|
@@ -146,14 +146,14 @@ export const QuickstartPage = () => {
|
|
|
146
146
|
</CardContent>
|
|
147
147
|
</Card>
|
|
148
148
|
))}
|
|
149
|
-
<Card className="hover:shadow-md transition-shadow rounded-md">
|
|
149
|
+
<Card className="hover:shadow-md transition-shadow rounded-md flex flex-col">
|
|
150
150
|
<CardHeader className="p-4 pb-0">
|
|
151
151
|
<CardTitle className="text-lg flex items-center justify-between">
|
|
152
152
|
JLCPCB Component
|
|
153
153
|
<TypeBadge type="package" className="ml-2" />
|
|
154
154
|
</CardTitle>
|
|
155
155
|
</CardHeader>
|
|
156
|
-
<CardContent className="p-4">
|
|
156
|
+
<CardContent className="p-4 mt-auto">
|
|
157
157
|
<Button
|
|
158
158
|
className="w-full"
|
|
159
159
|
onClick={() => setIsJLCPCBDialogOpen(true)}
|
|
@@ -162,14 +162,14 @@ export const QuickstartPage = () => {
|
|
|
162
162
|
</Button>
|
|
163
163
|
</CardContent>
|
|
164
164
|
</Card>
|
|
165
|
-
<Card className="hover:shadow-md transition-shadow rounded-md">
|
|
165
|
+
<Card className="hover:shadow-md transition-shadow rounded-md flex flex-col">
|
|
166
166
|
<CardHeader className="p-4 pb-0">
|
|
167
167
|
<CardTitle className="text-lg flex items-center justify-between">
|
|
168
168
|
Circuit Json
|
|
169
169
|
<TypeBadge type="module" className="ml-2" />
|
|
170
170
|
</CardTitle>
|
|
171
171
|
</CardHeader>
|
|
172
|
-
<CardContent className="p-4">
|
|
172
|
+
<CardContent className="p-4 mt-auto">
|
|
173
173
|
<Button
|
|
174
174
|
className="w-full"
|
|
175
175
|
onClick={() => setIsCircuitJsonImportDialogOpen(true)}
|
|
@@ -10,6 +10,14 @@ import { Button } from "@/components/ui/button"
|
|
|
10
10
|
import { GitHubLogoIcon, StarIcon } from "@radix-ui/react-icons"
|
|
11
11
|
import { Input } from "@/components/ui/input"
|
|
12
12
|
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
13
|
+
import { MoreVertical, Trash2 } from "lucide-react"
|
|
14
|
+
import { useConfirmDeleteSnippetDialog } from "@/components/dialogs/confirm-delete-snippet-dialog"
|
|
15
|
+
import {
|
|
16
|
+
DropdownMenu,
|
|
17
|
+
DropdownMenuContent,
|
|
18
|
+
DropdownMenuItem,
|
|
19
|
+
DropdownMenuTrigger,
|
|
20
|
+
} from "@/components/ui/dropdown-menu"
|
|
13
21
|
|
|
14
22
|
export const UserProfilePage = () => {
|
|
15
23
|
const { username } = useParams()
|
|
@@ -17,6 +25,9 @@ export const UserProfilePage = () => {
|
|
|
17
25
|
const [searchQuery, setSearchQuery] = useState("")
|
|
18
26
|
const session = useGlobalStore((s) => s.session)
|
|
19
27
|
const isCurrentUserProfile = username === session?.github_username
|
|
28
|
+
const { Dialog: DeleteDialog, openDialog: openDeleteDialog } =
|
|
29
|
+
useConfirmDeleteSnippetDialog()
|
|
30
|
+
const [snippetToDelete, setSnippetToDelete] = useState<Snippet | null>(null)
|
|
20
31
|
|
|
21
32
|
const { data: userSnippets, isLoading } = useQuery<Snippet[]>(
|
|
22
33
|
["userSnippets", username],
|
|
@@ -32,6 +43,12 @@ export const UserProfilePage = () => {
|
|
|
32
43
|
snippet.unscoped_name.toLowerCase().includes(searchQuery.toLowerCase()),
|
|
33
44
|
)
|
|
34
45
|
|
|
46
|
+
const handleDeleteClick = (e: React.MouseEvent, snippet: Snippet) => {
|
|
47
|
+
e.preventDefault() // Prevent navigation
|
|
48
|
+
setSnippetToDelete(snippet)
|
|
49
|
+
openDeleteDialog()
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
return (
|
|
36
53
|
<div>
|
|
37
54
|
<Header />
|
|
@@ -76,9 +93,33 @@ export const UserProfilePage = () => {
|
|
|
76
93
|
<h3 className="text-md font-semibold">
|
|
77
94
|
{snippet.unscoped_name}
|
|
78
95
|
</h3>
|
|
79
|
-
<div className="flex items-center
|
|
80
|
-
<
|
|
81
|
-
|
|
96
|
+
<div className="flex items-center gap-2">
|
|
97
|
+
<div className="flex items-center text-gray-600">
|
|
98
|
+
<StarIcon className="w-4 h-4 mr-1" />
|
|
99
|
+
<span>{snippet.star_count || 0}</span>
|
|
100
|
+
</div>
|
|
101
|
+
{isCurrentUserProfile && (
|
|
102
|
+
<DropdownMenu>
|
|
103
|
+
<DropdownMenuTrigger asChild>
|
|
104
|
+
<Button
|
|
105
|
+
variant="ghost"
|
|
106
|
+
size="icon"
|
|
107
|
+
className="h-8 w-8"
|
|
108
|
+
>
|
|
109
|
+
<MoreVertical className="h-4 w-4" />
|
|
110
|
+
</Button>
|
|
111
|
+
</DropdownMenuTrigger>
|
|
112
|
+
<DropdownMenuContent>
|
|
113
|
+
<DropdownMenuItem
|
|
114
|
+
className="text-xs text-red-600"
|
|
115
|
+
onClick={(e) => handleDeleteClick(e, snippet)}
|
|
116
|
+
>
|
|
117
|
+
<Trash2 className="mr-2 h-3 w-3" />
|
|
118
|
+
Delete Snippet
|
|
119
|
+
</DropdownMenuItem>
|
|
120
|
+
</DropdownMenuContent>
|
|
121
|
+
</DropdownMenu>
|
|
122
|
+
)}
|
|
82
123
|
</div>
|
|
83
124
|
</div>
|
|
84
125
|
<p className="text-sm text-gray-500">
|
|
@@ -91,6 +132,12 @@ export const UserProfilePage = () => {
|
|
|
91
132
|
</div>
|
|
92
133
|
)}
|
|
93
134
|
</div>
|
|
135
|
+
{snippetToDelete && (
|
|
136
|
+
<DeleteDialog
|
|
137
|
+
snippetId={snippetToDelete.snippet_id}
|
|
138
|
+
snippetName={snippetToDelete.unscoped_name}
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
94
141
|
<Footer />
|
|
95
142
|
</div>
|
|
96
143
|
)
|