@tscircuit/fake-snippets 0.0.53 → 0.0.55

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.
@@ -32,17 +32,14 @@ export default withRouteSpec({
32
32
  error: z.string().optional(),
33
33
  }),
34
34
  })(async (req, ctx) => {
35
- const { package_release_id, vendor_name } = req.jsonBody
35
+ const { package_release_id, vendor_name, circuit_json } = req.jsonBody
36
36
 
37
- // check package release exists
38
- const packageRelease = ctx.db.getPackageReleaseById(package_release_id)
39
- if (!packageRelease) {
40
- return ctx.json(
41
- {
42
- error: "Package release not found",
43
- },
44
- { status: 404 },
45
- )
37
+ if (package_release_id) {
38
+ // check package release exists
39
+ const packageRelease = ctx.db.getPackageReleaseById(package_release_id)
40
+ if (!packageRelease) {
41
+ return ctx.json({ error: "Package release not found" }, { status: 404 })
42
+ }
46
43
  }
47
44
 
48
45
  const orderQuoteId = ctx.db.addOrderQuote({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,7 @@ const SearchButtonComponent = () => {
17
17
  {isExpanded ? (
18
18
  <div className="flex items-center gap-2">
19
19
  <div className="w-32 bg-white">
20
- <SearchComponent />
20
+ <SearchComponent autofocus />
21
21
  </div>
22
22
  {/* <Button
23
23
  variant="ghost"
@@ -9,6 +9,7 @@ import { PrefetchPageLink } from "./PrefetchPageLink"
9
9
 
10
10
  interface SearchComponentProps {
11
11
  onResultsFetched?: (results: any[]) => void // optional
12
+ autofocus?: boolean
12
13
  }
13
14
 
14
15
  const LinkWithNewTabHandling = ({
@@ -43,6 +44,7 @@ const LinkWithNewTabHandling = ({
43
44
 
44
45
  const SearchComponent: React.FC<SearchComponentProps> = ({
45
46
  onResultsFetched,
47
+ autofocus = false,
46
48
  }) => {
47
49
  const [searchQuery, setSearchQuery] = useState("")
48
50
  const [showResults, setShowResults] = useState(false)
@@ -74,7 +76,9 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
74
76
 
75
77
  // Focus input on mount
76
78
  useEffect(() => {
77
- inputRef.current?.focus()
79
+ if (autofocus) {
80
+ inputRef.current?.focus()
81
+ }
78
82
  }, [])
79
83
 
80
84
  useEffect(() => {
@@ -1,37 +1,54 @@
1
+ import React, { useEffect } from "react"
1
2
  import Markdown from "react-markdown"
2
3
  import remarkGfm from "remark-gfm"
3
4
 
5
+ import { useShikiHighlighter } from "@/hooks/use-shiki-highlighter"
6
+
4
7
  export default function MarkdownViewer({
5
8
  markdownContent,
6
9
  }: {
7
10
  markdownContent: string
8
11
  }) {
12
+ const { highlighter } = useShikiHighlighter()
9
13
  return (
10
- <div className="prose dark:prose-invert prose-pre:bg-gray-100 dark:prose-pre:bg-gray-800 prose-code:font-mono markdown-content">
11
- <Markdown
12
- remarkPlugins={[remarkGfm]}
13
- components={{
14
- code({ node, className, children, ...props }) {
15
- const isCodeBlock =
16
- className?.includes("language-") || /\n/.test(String(children))
17
-
18
- // Don't use code tags cause of it's backticks not being removed
19
- return isCodeBlock ? (
20
- <div className="bg-gray-100 dark:bg-gray-800 rounded overflow-auto w-full">
21
- <span className="text-gray-800 dark:text-gray-200 font-mono whitespace-pre">
14
+ <div className="markdown-code">
15
+ <div className="prose dark:prose-invert prose-pre:py-0 prose-pre:px-6 prose-pre:bg-white dark:prose-pre:bg-gray-800 prose-code:font-mono markdown-content">
16
+ <Markdown
17
+ remarkPlugins={[remarkGfm]}
18
+ components={{
19
+ code({ node, className, children, ...props }) {
20
+ const isCodeBlock =
21
+ className?.includes("language-") || /\n/.test(String(children))
22
+ const dom = document.createElement("div")
23
+ if (highlighter) {
24
+ dom.innerHTML = highlighter.codeToHtml(
25
+ children?.toString() || "",
26
+ {
27
+ lang: "tsx",
28
+ themes: {
29
+ light: "github-light",
30
+ dark: "github-dark",
31
+ },
32
+ },
33
+ )
34
+ }
35
+ // Don’t use <code> tags (they leave backticks intact)
36
+ return isCodeBlock ? (
37
+ <div
38
+ dangerouslySetInnerHTML={{ __html: dom.innerHTML }}
39
+ className="border rounded-lg"
40
+ ></div>
41
+ ) : (
42
+ <span className="bg-gray-100 dark:bg-gray-800 text-gray-800 font-semibold font-mono dark:text-gray-200 px-1 py-0.5 rounded">
22
43
  {children}
23
44
  </span>
24
- </div>
25
- ) : (
26
- <span className="bg-gray-100 dark:bg-gray-800 text-gray-800 font-semibold font-mono dark:text-gray-200 px-1 py-0.5 rounded">
27
- {children}
28
- </span>
29
- )
30
- },
31
- }}
32
- >
33
- {markdownContent}
34
- </Markdown>
45
+ )
46
+ },
47
+ }}
48
+ >
49
+ {markdownContent}
50
+ </Markdown>
51
+ </div>
35
52
  </div>
36
53
  )
37
54
  }
@@ -321,7 +321,7 @@ export function CodeAndPreview({ pkg }: Props) {
321
321
  state.pkgFilesWithContent,
322
322
  ])
323
323
 
324
- if ((!pkg && urlParams.package_id) || pkgFiles.isLoading) {
324
+ if ((!pkg && urlParams.package_id) || pkgFiles.isLoading || isLoadingFiles) {
325
325
  return (
326
326
  <div className="flex items-center justify-center h-64">
327
327
  <div className="flex flex-col items-center justify-center">
@@ -187,7 +187,8 @@ export default function EditorNav({
187
187
  }
188
188
 
189
189
  const canSavePackage = Boolean(
190
- isLoggedIn && pkg?.owner_github_username === session?.github_username,
190
+ isLoggedIn &&
191
+ (!pkg || pkg?.owner_github_username === session?.github_username),
191
192
  )
192
193
  return (
193
194
  <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">
@@ -250,7 +251,7 @@ export default function EditorNav({
250
251
  variant="outline"
251
252
  size="sm"
252
253
  className={"ml-1 h-6 px-2 text-xs save-button"}
253
- disabled={canSavePackage ? !hasUnsavedChanges : !isLoggedIn}
254
+ disabled={canSavePackage && pkg ? !hasUnsavedChanges : !isLoggedIn}
254
255
  onClick={canSavePackage ? onSave : () => forkSnippet()}
255
256
  >
256
257
  {canSavePackage ? (
package/src/index.css CHANGED
@@ -40,3 +40,17 @@
40
40
  text-align: right;
41
41
  color: rgba(115, 138, 148, 0.4);
42
42
  }
43
+
44
+ .markdown-code .shiki .line::before {
45
+ content: none !important;
46
+ display: none !important;
47
+ }
48
+
49
+ .markdown-code .shiki .line {
50
+ padding-left: 0 !important;
51
+ }
52
+
53
+ .markdown-code pre {
54
+ padding: 12px !important;
55
+ margin: 0 !important;
56
+ }
@@ -10,10 +10,6 @@ export const handleManualEditsImportWithSupportForMultipleFiles = (
10
10
  variant?: "default" | "destructive"
11
11
  }) => void,
12
12
  ) => {
13
- console.log(
14
- "handleManualEditsImportWithSupportForMultipleFiles",
15
- entrypointFileName,
16
- )
17
13
  try {
18
14
  let currentContent = files[entrypointFileName]
19
15
  const importRegex =
@@ -1,10 +1,15 @@
1
1
  import posthog from "posthog-js"
2
2
 
3
- if (!posthog.__loaded) {
4
- posthog.init("phc_htd8AQjSfVEsFCLQMAiUooG4Q0DKBCjqYuQglc9V3Wo", {
5
- api_host: "https://postpig.tscircuit.com",
6
- person_profiles: "always",
7
- })
3
+ if (
4
+ !window.location.hostname.includes("localhost") &&
5
+ !window.location.hostname.includes("127.0.0.1")
6
+ ) {
7
+ if (!posthog.__loaded) {
8
+ posthog.init("phc_htd8AQjSfVEsFCLQMAiUooG4Q0DKBCjqYuQglc9V3Wo", {
9
+ api_host: "https://postpig.tscircuit.com",
10
+ person_profiles: "always",
11
+ })
12
+ }
8
13
  }
9
14
 
10
15
  export { posthog }
@@ -6,32 +6,57 @@ 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, ChevronDown, ChevronUp } from "lucide-react"
9
+ import {
10
+ Edit2,
11
+ Star,
12
+ ChevronDown,
13
+ ChevronUp,
14
+ Key,
15
+ KeyRound,
16
+ } from "lucide-react"
10
17
  import { Button } from "@/components/ui/button"
11
18
  import { useGlobalStore } from "@/hooks/use-global-store"
12
19
  import { PrefetchPageLink } from "@/components/PrefetchPageLink"
13
20
  import { SnippetList } from "@/components/SnippetList"
14
21
  import { Helmet } from "react-helmet-async"
22
+ import { useSignIn } from "@/hooks/use-sign-in"
23
+ import { SnippetCard } from "@/components/SnippetCard"
24
+ import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
25
+ import { useConfirmDeletePackageDialog } from "@/components/dialogs/confirm-delete-package-dialog"
15
26
 
16
27
  export const DashboardPage = () => {
17
28
  const axios = useAxios()
18
29
 
19
30
  const currentUser = useGlobalStore((s) => s.session?.github_username)
31
+ const isLoggedIn = Boolean(currentUser)
32
+ const signIn = useSignIn()
33
+
20
34
  const [showAllTrending, setShowAllTrending] = useState(false)
21
35
  const [showAllLatest, setShowAllLatest] = useState(false)
36
+ const [snippetToDelete, setSnippetToDelete] = useState<Snippet | null>(null)
37
+ const { Dialog: DeleteDialog, openDialog: openDeleteDialog } =
38
+ useConfirmDeletePackageDialog()
22
39
 
23
40
  const {
24
41
  data: mySnippets,
25
42
  isLoading,
26
43
  error,
27
- } = useQuery<Snippet[]>("userSnippets", async () => {
28
- const response = await axios.get(`/snippets/list?owner_name=${currentUser}`)
29
- return response.data.snippets.sort(
30
- (a: any, b: any) =>
31
- new Date(b.updated_at || b.created_at).getTime() -
32
- new Date(a.updated_at || a.created_at).getTime(),
33
- )
34
- })
44
+ } = useQuery<Snippet[]>(
45
+ "userSnippets",
46
+ async () => {
47
+ const response = await axios.get(
48
+ `/snippets/list?owner_name=${currentUser}`,
49
+ )
50
+ return response.data.snippets.sort(
51
+ (a: any, b: any) =>
52
+ new Date(b.updated_at || b.created_at).getTime() -
53
+ new Date(a.updated_at || a.created_at).getTime(),
54
+ )
55
+ },
56
+ {
57
+ enabled: isLoggedIn,
58
+ },
59
+ )
35
60
 
36
61
  const { data: trendingSnippets } = useQuery<Snippet[]>(
37
62
  "trendingSnippets",
@@ -49,6 +74,13 @@ export const DashboardPage = () => {
49
74
  },
50
75
  )
51
76
 
77
+ const baseUrl = useSnippetsBaseApiUrl()
78
+
79
+ const handleDeleteClick = (e: React.MouseEvent, snippet: Snippet) => {
80
+ e.preventDefault() // Prevent navigation
81
+ setSnippetToDelete(snippet)
82
+ openDeleteDialog()
83
+ }
52
84
  return (
53
85
  <div>
54
86
  <Helmet>
@@ -59,77 +91,120 @@ export const DashboardPage = () => {
59
91
  <h1 className="text-3xl font-bold mb-6">Dashboard</h1>
60
92
  <div className="flex md:flex-row flex-col">
61
93
  <div className="md:w-3/4 p-0 md:pr-6">
62
- <div className="mt-6 mb-4">
63
- <div className="flex items-center">
64
- <h2 className="text-sm text-gray-600 whitespace-nowrap">
65
- Edit Recent
94
+ {!isLoggedIn ? (
95
+ <div className="flex flex-col items-center justify-center h-64 rounded-md p-4 mt-[40px] mb-2 sm:mb-4">
96
+ <div className="p-4 mb-4 rounded-full bg-blue-50 border border-blue-100 shadow-sm">
97
+ <KeyRound className="text-blue-500" size={32} />
98
+ </div>
99
+ <h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-3">
100
+ You're not logged in
101
+ </h2>
102
+
103
+ <p className="text-gray-600 mb-6 text-center max-w-md text-sm sm:text-base">
104
+ Log in to access your dashboard and manage your snippets.
105
+ </p>
106
+ <Button onClick={() => signIn()} variant="outline">
107
+ Log in
108
+ </Button>
109
+ </div>
110
+ ) : (
111
+ <>
112
+ <div className="mt-6 mb-4">
113
+ <div className="flex items-center">
114
+ <h2 className="text-sm text-gray-600 whitespace-nowrap">
115
+ Edit Recent
116
+ </h2>
117
+ <div className="flex gap-2 items-center overflow-x-scroll md:overflow-hidden">
118
+ {mySnippets &&
119
+ mySnippets.slice(0, 3).map((snippet) => (
120
+ <div key={snippet.snippet_id}>
121
+ <PrefetchPageLink
122
+ href={`/editor?snippet_id=${snippet.snippet_id}`}
123
+ className="text-blue-600 hover:underline"
124
+ >
125
+ <Button
126
+ variant="ghost"
127
+ size="sm"
128
+ className="font-medium"
129
+ >
130
+ {snippet.unscoped_name}
131
+ <Edit2 className="w-3 h-3 ml-2" />
132
+ </Button>
133
+ </PrefetchPageLink>
134
+ </div>
135
+ ))}
136
+ </div>
137
+ </div>
138
+ </div>
139
+ {/* <CreateNewSnippetWithAiHero/> */}
140
+ <h2 className="text-sm font-bold mb-2 text-gray-700 border-b border-gray-200">
141
+ Your Recent Packages
66
142
  </h2>
67
- <div className="flex gap-2 items-center overflow-x-scroll md:overflow-hidden ">
68
- {mySnippets &&
69
- mySnippets.slice(0, 3).map((snippet) => (
70
- <div key={snippet.snippet_id}>
71
- <PrefetchPageLink
72
- href={`/editor?snippet_id=${snippet.snippet_id}`}
73
- className="text-blue-600 hover:underline"
74
- >
75
- <Button
76
- variant="ghost"
77
- size="sm"
78
- className="font-medium"
79
- >
80
- {snippet.unscoped_name}
81
- <Edit2 className="w-3 h-3 ml-2" />
82
- </Button>
83
- </PrefetchPageLink>
143
+ {isLoading && (
144
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
145
+ {[...Array(6)].map((_, i) => (
146
+ <div
147
+ key={i}
148
+ className="border p-4 rounded-md animate-pulse"
149
+ >
150
+ <div className="flex items-start gap-4">
151
+ <div className="h-16 w-16 flex-shrink-0 rounded-md bg-slate-200"></div>
152
+ <div className="flex-1">
153
+ <div className="h-5 bg-slate-200 rounded w-3/4 mb-2"></div>
154
+ <div className="h-4 bg-slate-200 rounded w-1/2 mb-2"></div>
155
+ <div className="flex gap-2">
156
+ <div className="h-3 bg-slate-200 rounded w-16"></div>
157
+ <div className="h-3 bg-slate-200 rounded w-16"></div>
158
+ </div>
159
+ </div>
160
+ </div>
84
161
  </div>
85
162
  ))}
86
- </div>
87
- </div>
88
- </div>
89
- <CreateNewSnippetWithAiHero />
90
- <h2 className="text-sm font-bold mb-2 text-gray-700 border-b border-gray-200">
91
- Your Recent Snippets
92
- </h2>
93
- {isLoading && <div>Loading...</div>}
94
- {mySnippets && (
95
- <ul className="space-y-1">
96
- {mySnippets.slice(0, 10).map((snippet) => (
97
- <li
98
- key={snippet.snippet_id}
99
- className="flex items-center justify-between"
100
- >
101
- <Link
102
- href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
103
- className="text-blue-600 hover:underline text-sm"
104
- >
105
- {snippet.unscoped_name}
106
- </Link>
107
- <span className="text-xs text-gray-500">
108
- {new Date(snippet.created_at).toLocaleDateString()}
163
+ </div>
164
+ )}
165
+ {mySnippets && mySnippets.length > 0 ? (
166
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
167
+ {mySnippets.slice(0, 10).map((snippet) => (
168
+ <SnippetCard
169
+ key={snippet.snippet_id}
170
+ snippet={snippet}
171
+ baseUrl={baseUrl}
172
+ isCurrentUserSnippet={
173
+ snippet.owner_name === currentUser
174
+ }
175
+ onDeleteClick={handleDeleteClick}
176
+ />
177
+ ))}
178
+ </div>
179
+ ) : (
180
+ !isLoading &&
181
+ mySnippets?.length === 0 && (
182
+ <span className="font-medium text-sm text-gray-500">
183
+ No packages found
109
184
  </span>
110
- </li>
111
- ))}
112
- </ul>
113
- )}
114
- {mySnippets && mySnippets.length > 10 && (
115
- <Link
116
- href={`/${currentUser}`}
117
- className="text-sm text-blue-600 hover:underline mt-2 inline-block"
118
- >
119
- View all snippets
120
- </Link>
185
+ )
186
+ )}
187
+ {mySnippets && mySnippets.length > 10 && (
188
+ <Link
189
+ href={`/${currentUser}`}
190
+ className="text-sm text-blue-600 hover:underline mt-2 inline-block"
191
+ >
192
+ View all packages
193
+ </Link>
194
+ )}
195
+ </>
121
196
  )}
122
197
  </div>
123
198
  <div className="md:w-1/4">
124
199
  <SnippetList
125
- title="Trending Snippets"
200
+ title="Trending Packages"
126
201
  snippets={trendingSnippets}
127
202
  showAll={showAllTrending}
128
203
  onToggleShowAll={() => setShowAllTrending(!showAllTrending)}
129
204
  />
130
205
  <div className="mt-8">
131
206
  <SnippetList
132
- title="Latest Snippets"
207
+ title="Latest Packages"
133
208
  snippets={latestSnippets}
134
209
  showAll={showAllLatest}
135
210
  onToggleShowAll={() => setShowAllLatest(!showAllLatest)}
@@ -137,6 +212,12 @@ export const DashboardPage = () => {
137
212
  </div>
138
213
  </div>
139
214
  </div>
215
+ {snippetToDelete && (
216
+ <DeleteDialog
217
+ packageId={snippetToDelete.snippet_id}
218
+ packageName={snippetToDelete.unscoped_name}
219
+ />
220
+ )}
140
221
  </div>
141
222
  <Footer />
142
223
  </div>
@@ -22,7 +22,7 @@ import {
22
22
  SelectTrigger,
23
23
  SelectValue,
24
24
  } from "@/components/ui/select"
25
- import { Star } from "lucide-react"
25
+ import { Box, Star } from "lucide-react"
26
26
 
27
27
  export const UserProfilePage = () => {
28
28
  const { username } = useParams()
@@ -188,10 +188,21 @@ export const UserProfilePage = () => {
188
188
  ) : (
189
189
  <div className="col-span-full flex justify-center">
190
190
  <div className="flex flex-col items-center py-12 text-gray-500">
191
- <Star />
192
- <span className="text-lg font-medium">
193
- No starred packages
194
- </span>
191
+ {activeTab === "starred" ? (
192
+ <>
193
+ <Star />
194
+ <span className="text-lg font-medium">
195
+ No starred packages
196
+ </span>
197
+ </>
198
+ ) : (
199
+ <>
200
+ <Box />
201
+ <span className="text-lg font-medium">
202
+ No packages available
203
+ </span>
204
+ </>
205
+ )}
195
206
  </div>
196
207
  </div>
197
208
  )}