@tscircuit/fake-snippets 0.0.49 → 0.0.51

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/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@ declare const snippetSchema: z.ZodObject<{
16
16
  manual_edits_json_content: z.ZodNullable<z.ZodOptional<z.ZodString>>;
17
17
  created_at: z.ZodString;
18
18
  updated_at: z.ZodString;
19
+ starred_at: z.ZodOptional<z.ZodString>;
19
20
  snippet_type: z.ZodEnum<["board", "package", "model", "footprint"]>;
20
21
  description: z.ZodOptional<z.ZodString>;
21
22
  version: z.ZodDefault<z.ZodString>;
@@ -44,6 +45,7 @@ declare const snippetSchema: z.ZodObject<{
44
45
  compiled_js?: string | null | undefined;
45
46
  circuit_json?: Record<string, any>[] | null | undefined;
46
47
  manual_edits_json_content?: string | null | undefined;
48
+ starred_at?: string | undefined;
47
49
  description?: string | undefined;
48
50
  tags?: string[] | undefined;
49
51
  }, {
@@ -61,6 +63,7 @@ declare const snippetSchema: z.ZodObject<{
61
63
  compiled_js?: string | null | undefined;
62
64
  circuit_json?: Record<string, any>[] | null | undefined;
63
65
  manual_edits_json_content?: string | null | undefined;
66
+ starred_at?: string | undefined;
64
67
  description?: string | undefined;
65
68
  version?: string | undefined;
66
69
  star_count?: number | undefined;
@@ -676,6 +679,7 @@ declare const createDatabase: ({ seed }?: {
676
679
  compiled_js?: string | null | undefined;
677
680
  circuit_json?: Record<string, any>[] | null | undefined;
678
681
  manual_edits_json_content?: string | null | undefined;
682
+ starred_at?: string | undefined;
679
683
  description?: string | undefined;
680
684
  tags?: string[] | undefined;
681
685
  }[];
@@ -960,6 +964,7 @@ declare const createDatabase: ({ seed }?: {
960
964
  compiled_js?: string | null | undefined;
961
965
  circuit_json?: Record<string, any>[] | null | undefined;
962
966
  manual_edits_json_content?: string | null | undefined;
967
+ starred_at?: string | undefined;
963
968
  description?: string | undefined;
964
969
  tags?: string[] | undefined;
965
970
  }[];
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ var snippetSchema = z.object({
26
26
  manual_edits_json_content: z.string().optional().nullable(),
27
27
  created_at: z.string(),
28
28
  updated_at: z.string(),
29
+ starred_at: z.string().optional(),
29
30
  snippet_type: z.enum(["board", "package", "model", "footprint"]),
30
31
  description: z.string().optional(),
31
32
  version: z.string().default("0.0.1"),
package/dist/schema.d.ts CHANGED
@@ -50,6 +50,7 @@ declare const snippetSchema: z.ZodObject<{
50
50
  manual_edits_json_content: z.ZodNullable<z.ZodOptional<z.ZodString>>;
51
51
  created_at: z.ZodString;
52
52
  updated_at: z.ZodString;
53
+ starred_at: z.ZodOptional<z.ZodString>;
53
54
  snippet_type: z.ZodEnum<["board", "package", "model", "footprint"]>;
54
55
  description: z.ZodOptional<z.ZodString>;
55
56
  version: z.ZodDefault<z.ZodString>;
@@ -78,6 +79,7 @@ declare const snippetSchema: z.ZodObject<{
78
79
  compiled_js?: string | null | undefined;
79
80
  circuit_json?: Record<string, any>[] | null | undefined;
80
81
  manual_edits_json_content?: string | null | undefined;
82
+ starred_at?: string | undefined;
81
83
  description?: string | undefined;
82
84
  tags?: string[] | undefined;
83
85
  }, {
@@ -95,6 +97,7 @@ declare const snippetSchema: z.ZodObject<{
95
97
  compiled_js?: string | null | undefined;
96
98
  circuit_json?: Record<string, any>[] | null | undefined;
97
99
  manual_edits_json_content?: string | null | undefined;
100
+ starred_at?: string | undefined;
98
101
  description?: string | undefined;
99
102
  version?: string | undefined;
100
103
  star_count?: number | undefined;
@@ -792,6 +795,7 @@ declare const databaseSchema: z.ZodObject<{
792
795
  manual_edits_json_content: z.ZodNullable<z.ZodOptional<z.ZodString>>;
793
796
  created_at: z.ZodString;
794
797
  updated_at: z.ZodString;
798
+ starred_at: z.ZodOptional<z.ZodString>;
795
799
  snippet_type: z.ZodEnum<["board", "package", "model", "footprint"]>;
796
800
  description: z.ZodOptional<z.ZodString>;
797
801
  version: z.ZodDefault<z.ZodString>;
@@ -820,6 +824,7 @@ declare const databaseSchema: z.ZodObject<{
820
824
  compiled_js?: string | null | undefined;
821
825
  circuit_json?: Record<string, any>[] | null | undefined;
822
826
  manual_edits_json_content?: string | null | undefined;
827
+ starred_at?: string | undefined;
823
828
  description?: string | undefined;
824
829
  tags?: string[] | undefined;
825
830
  }, {
@@ -837,6 +842,7 @@ declare const databaseSchema: z.ZodObject<{
837
842
  compiled_js?: string | null | undefined;
838
843
  circuit_json?: Record<string, any>[] | null | undefined;
839
844
  manual_edits_json_content?: string | null | undefined;
845
+ starred_at?: string | undefined;
840
846
  description?: string | undefined;
841
847
  version?: string | undefined;
842
848
  star_count?: number | undefined;
@@ -1455,6 +1461,7 @@ declare const databaseSchema: z.ZodObject<{
1455
1461
  compiled_js?: string | null | undefined;
1456
1462
  circuit_json?: Record<string, any>[] | null | undefined;
1457
1463
  manual_edits_json_content?: string | null | undefined;
1464
+ starred_at?: string | undefined;
1458
1465
  description?: string | undefined;
1459
1466
  tags?: string[] | undefined;
1460
1467
  }[];
@@ -1661,6 +1668,7 @@ declare const databaseSchema: z.ZodObject<{
1661
1668
  compiled_js?: string | null | undefined;
1662
1669
  circuit_json?: Record<string, any>[] | null | undefined;
1663
1670
  manual_edits_json_content?: string | null | undefined;
1671
+ starred_at?: string | undefined;
1664
1672
  description?: string | undefined;
1665
1673
  version?: string | undefined;
1666
1674
  star_count?: number | undefined;
package/dist/schema.js CHANGED
@@ -21,6 +21,7 @@ var snippetSchema = z.object({
21
21
  manual_edits_json_content: z.string().optional().nullable(),
22
22
  created_at: z.string(),
23
23
  updated_at: z.string(),
24
+ starred_at: z.string().optional(),
24
25
  snippet_type: z.enum(["board", "package", "model", "footprint"]),
25
26
  description: z.string().optional(),
26
27
  version: z.string().default("0.0.1"),
@@ -25,6 +25,7 @@ export const snippetSchema = z.object({
25
25
  manual_edits_json_content: z.string().optional().nullable(),
26
26
  created_at: z.string(),
27
27
  updated_at: z.string(),
28
+ starred_at: z.string().optional(),
28
29
  snippet_type: z.enum(["board", "package", "model", "footprint"]),
29
30
  description: z.string().optional(),
30
31
  version: z.string().default("0.0.1"),
@@ -9,11 +9,23 @@ export default withRouteSpec({
9
9
  vendor_name: z.string(),
10
10
  }),
11
11
  jsonResponse: z.object({
12
- order_quote_id: z.string(),
12
+ order_quote_id: z.string().optional(),
13
+ error: z.string().optional(),
13
14
  }),
14
15
  })(async (req, ctx) => {
15
16
  const { package_release_id, vendor_name } = req.jsonBody
16
17
 
18
+ // check package release exists
19
+ const packageRelease = ctx.db.getPackageReleaseById(package_release_id)
20
+ if (!packageRelease) {
21
+ return ctx.json(
22
+ {
23
+ error: "Package release not found",
24
+ },
25
+ { status: 404 },
26
+ )
27
+ }
28
+
17
29
  const orderQuoteId = ctx.db.addOrderQuote({
18
30
  account_id: ctx.auth.account_id,
19
31
  package_release_id,
@@ -66,12 +66,14 @@ export default withRouteSpec({
66
66
  existing.updated_at = new Date().toISOString()
67
67
  } else {
68
68
  // Add star by creating a new account_package record
69
+ const newTimestamp = new Date().toISOString()
69
70
  const newAccountPackage = {
71
+ account_package_id: `ap_${Date.now()}`,
70
72
  account_id: ctx.auth.account_id,
71
73
  package_id: packageId,
72
74
  is_starred: true,
73
- created_at: new Date().toISOString(),
74
- updated_at: new Date().toISOString(),
75
+ created_at: newTimestamp,
76
+ updated_at: newTimestamp,
75
77
  }
76
78
  ctx.db.addAccountPackage(newAccountPackage)
77
79
  }
@@ -49,11 +49,22 @@ export default withRouteSpec({
49
49
  packages = packages.filter((p) => p.owner_org_id === auth.personal_org_id)
50
50
  }
51
51
 
52
+ // Get star timestamps for authenticated user
53
+ const starTimestamps = new Map<string, string>()
54
+ if (auth) {
55
+ ctx.db.accountPackages
56
+ .filter((ap) => ap.account_id === auth.account_id && ap.is_starred)
57
+ .forEach((ap) => {
58
+ starTimestamps.set(ap.package_id, ap.updated_at)
59
+ })
60
+ }
61
+
52
62
  return ctx.json({
53
63
  ok: true,
54
64
  packages: packages.map((p) => ({
55
65
  ...p,
56
66
  latest_package_release_id: p.latest_package_release_id || null,
67
+ starred_at: starTimestamps.get(p.package_id) || null,
57
68
  })),
58
69
  })
59
70
  })
@@ -1,6 +1,6 @@
1
1
  import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
2
2
  import { z } from "zod"
3
- import { snippetSchema } from "fake-snippets-api/lib/db/schema"
3
+ import { snippetSchema, type Account } from "fake-snippets-api/lib/db/schema"
4
4
 
5
5
  export default withRouteSpec({
6
6
  methods: ["GET", "POST"],
@@ -17,7 +17,7 @@ export default withRouteSpec({
17
17
  }),
18
18
  })(async (req, ctx) => {
19
19
  const { owner_name, unscoped_name, starred_by } = req.commonParams
20
-
20
+ let starredByAccount: Account | null = null
21
21
  // Get all packages that are snippets
22
22
  let packages = ctx.db.packages.filter((pkg) => pkg.is_snippet === true)
23
23
 
@@ -38,25 +38,44 @@ export default withRouteSpec({
38
38
 
39
39
  // Filter by starred_by if provided
40
40
  if (starred_by) {
41
- // Get the account ID for the starred_by username
42
- const starredByAccount = ctx.db.accounts.find(
43
- (acc) => acc.github_username.toLowerCase() === starred_by.toLowerCase(),
44
- )
41
+ starredByAccount =
42
+ ctx.db.accounts.find(
43
+ (acc) => acc.github_username.toLowerCase() === starred_by.toLowerCase(),
44
+ ) || null
45
+
45
46
  if (starredByAccount) {
46
- // Filter packages to only include those that are starred by this account
47
- packages = packages.filter((pkg) =>
48
- ctx.db.hasStarred(starredByAccount.account_id, pkg.package_id),
47
+ const accountPackages = ctx.db.accountPackages.filter(
48
+ (ap) => ap.account_id === starredByAccount?.account_id && ap.is_starred,
49
49
  )
50
+
51
+ const starTimestamps = new Map(
52
+ accountPackages.map((ap) => [ap.package_id, ap.updated_at]),
53
+ )
54
+
55
+ // Filter packages to only include starred ones
56
+ packages = packages.filter((pkg) => starTimestamps.has(pkg.package_id))
50
57
  } else {
51
- // If we can't find the account, return no snippets
52
58
  packages = []
53
59
  }
54
60
  }
55
61
 
62
+ // Convert packages to snippets
56
63
  const snippets = packages.map((pkg) => {
57
64
  const packageRelease = ctx.db.getPackageReleaseById(
58
65
  pkg.latest_package_release_id || "",
59
66
  )
67
+
68
+ let starTimestamp
69
+ if (starred_by && starredByAccount) {
70
+ const accountPackage = ctx.db.accountPackages.find(
71
+ (ap) =>
72
+ ap.account_id === starredByAccount.account_id &&
73
+ ap.package_id === pkg.package_id &&
74
+ ap.is_starred,
75
+ )
76
+ starTimestamp = accountPackage?.updated_at
77
+ }
78
+
60
79
  if (!packageRelease) {
61
80
  return {
62
81
  snippet_id: pkg.package_id,
@@ -71,6 +90,7 @@ export default withRouteSpec({
71
90
  compiled_js: "",
72
91
  created_at: pkg.created_at,
73
92
  updated_at: pkg.updated_at,
93
+ starred_at: starTimestamp, // Add star timestamp
74
94
  star_count: ctx.db.getStarCount(pkg.package_id),
75
95
  is_starred: ctx.auth
76
96
  ? ctx.db.hasStarred(ctx.auth.account_id, pkg.package_id)
@@ -81,6 +101,7 @@ export default withRouteSpec({
81
101
  is_unlisted: pkg.is_unlisted || false,
82
102
  }
83
103
  }
104
+
84
105
  const packageFiles = ctx.db.getPackageFilesByReleaseId(
85
106
  packageRelease.package_release_id,
86
107
  )
@@ -88,12 +109,8 @@ export default withRouteSpec({
88
109
  (file: { file_path: string }) =>
89
110
  file.file_path === "index.ts" || file.file_path === "index.tsx",
90
111
  )
91
- const starCount = ctx.db.getStarCount(pkg.package_id)
92
- const isStarred = ctx.auth
93
- ? ctx.db.hasStarred(ctx.auth.account_id, pkg.package_id)
94
- : false
95
112
 
96
- return {
113
+ const snippet = {
97
114
  snippet_id: pkg.package_id,
98
115
  package_release_id: pkg.latest_package_release_id || "",
99
116
  unscoped_name: pkg.unscoped_name,
@@ -113,13 +130,18 @@ export default withRouteSpec({
113
130
  )?.content_text || "",
114
131
  created_at: pkg.created_at,
115
132
  updated_at: pkg.updated_at,
116
- star_count: starCount,
117
- is_starred: isStarred,
133
+ starred_at: starTimestamp, // Add star timestamp
134
+ star_count: ctx.db.getStarCount(pkg.package_id),
135
+ is_starred: ctx.auth
136
+ ? ctx.db.hasStarred(ctx.auth.account_id, pkg.package_id)
137
+ : false,
118
138
  version: pkg.latest_version || "0.0.1",
119
139
  is_private: pkg.is_private || false,
120
140
  is_public: pkg.is_public || true,
121
141
  is_unlisted: pkg.is_unlisted || false,
122
142
  }
143
+
144
+ return snippet
123
145
  })
124
146
 
125
147
  return ctx.json({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,7 +18,7 @@
18
18
  "playwright": "bunx playwright test",
19
19
  "playwright:update": "bunx playwright test --update-snapshots",
20
20
  "start:playwright-server": "bun run build:fake-api && vite --port 5177",
21
- "dev": "bun run build:fake-api && AUTOLOAD_SNIPPETS=true vite",
21
+ "dev": "bun run build:fake-api && AUTOLOAD_PACKAGES=true vite",
22
22
  "dev:registry": "SNIPPETS_API_URL=http://localhost:3100 vite",
23
23
  "build": "bun run generate-images && bun run generate-sitemap && bun run build:fake-api && tsc -b && vite build",
24
24
  "preview": "vite preview",
@@ -120,9 +120,11 @@
120
120
  "react-hook-form": "^7.53.0",
121
121
  "react-hot-toast": "^2.5.2",
122
122
  "react-intersection-observer": "^9.14.1",
123
+ "react-markdown": "^10.1.0",
123
124
  "react-query": "^3.39.3",
124
125
  "react-resizable-panels": "^2.1.3",
125
126
  "recharts": "^2.12.7",
127
+ "remark-gfm": "^4.0.1",
126
128
  "rollup-plugin-visualizer": "^5.12.0",
127
129
  "sitemap": "^8.0.0",
128
130
  "sonner": "^1.5.0",
@@ -140,9 +142,10 @@
140
142
  "@babel/standalone": "^7.26.2",
141
143
  "@biomejs/biome": "^1.9.2",
142
144
  "@playwright/test": "^1.48.0",
145
+ "@tailwindcss/typography": "^0.5.16",
143
146
  "@tscircuit/core": "^0.0.370",
144
147
  "@tscircuit/prompt-benchmarks": "^0.0.28",
145
- "@tscircuit/runframe": "^0.0.361",
148
+ "@tscircuit/runframe": "^0.0.370",
146
149
  "@types/babel__standalone": "^7.1.7",
147
150
  "@types/bun": "^1.1.10",
148
151
  "@types/country-list": "^2.1.4",
@@ -1,12 +1,67 @@
1
1
  import { QueryClient, QueryClientProvider } from "react-query"
2
2
  import { HelmetProvider } from "react-helmet-async"
3
+ import { useEffect } from "react"
4
+ import { useGlobalStore } from "./hooks/use-global-store"
5
+ import { posthog } from "./lib/posthog"
6
+
7
+ const staffGithubUsernames = [
8
+ "imrishabh18",
9
+ "seveibar",
10
+ "testuser",
11
+ ...(import.meta.env.VITE_STAFF_GITHUB_USERNAMES?.split(",") || []),
12
+ ]
3
13
 
4
14
  const queryClient = new QueryClient()
5
15
 
16
+ const isInternalGithubUser = (githubUsername?: string | null) => {
17
+ if (!githubUsername) return false
18
+ return staffGithubUsernames.some(
19
+ (internalGithubUsername: string) =>
20
+ internalGithubUsername.toLowerCase() === githubUsername.toLowerCase(),
21
+ )
22
+ }
23
+
24
+ function PostHogIdentifier() {
25
+ const session = useGlobalStore((s) => s.session)
26
+
27
+ useEffect(() => {
28
+ if (!posthog.__loaded) {
29
+ const checkInterval = setInterval(() => {
30
+ if (posthog.__loaded) {
31
+ clearInterval(checkInterval)
32
+ identifyUser()
33
+ }
34
+ }, 100)
35
+ return () => clearInterval(checkInterval)
36
+ }
37
+
38
+ const identifyUser = async () => {
39
+ try {
40
+ const githubUsername = session?.github_username
41
+
42
+ if (isInternalGithubUser(githubUsername)) {
43
+ posthog.identify(session?.github_username, {
44
+ is_tscircuit_staff: true,
45
+ })
46
+ }
47
+ } catch (error) {
48
+ // Error handling silently fails
49
+ }
50
+ }
51
+
52
+ identifyUser()
53
+ }, [session])
54
+
55
+ return null
56
+ }
57
+
6
58
  export const ContextProviders = ({ children }: any) => {
7
59
  return (
8
60
  <QueryClientProvider client={queryClient}>
9
- <HelmetProvider>{children}</HelmetProvider>
61
+ <HelmetProvider>
62
+ <PostHogIdentifier />
63
+ {children}
64
+ </HelmetProvider>
10
65
  </QueryClientProvider>
11
66
  )
12
67
  }
@@ -1,11 +1,4 @@
1
1
  import { Analytics as VercelAnalytics } from "@vercel/analytics/react"
2
- import posthog from "posthog-js"
3
- import CookieConsent from "react-cookie-consent"
4
-
5
- posthog.init("phc_htd8AQjSfVEsFCLQMAiUooG4Q0DKBCjqYuQglc9V3Wo", {
6
- api_host: "https://postpig.tscircuit.com",
7
- person_profiles: "always",
8
- })
9
2
 
10
3
  export const Analytics = () => {
11
4
  return (
@@ -33,6 +33,9 @@ import React from "@types/react/jsx-runtime"
33
33
  import { Circuit, createUseComponent } from "@tscircuit/core"
34
34
  import type { CommonLayoutProps } from "@tscircuit/props"
35
35
  `
36
+ import { getSingletonHighlighter, Highlighter } from "shiki"
37
+ import { useShikiHighlighter } from "@/hooks/use-shiki-highlighter"
38
+
36
39
  export const CodeEditor = ({
37
40
  onCodeChange,
38
41
  onDtsChange,
@@ -58,6 +61,8 @@ export const CodeEditor = ({
58
61
  const apiUrl = useSnippetsBaseApiUrl()
59
62
  const codeCompletionApi = useCodeCompletionApi()
60
63
 
64
+ const { highlighter, isLoading } = useShikiHighlighter()
65
+
61
66
  const [cursorPosition, setCursorPosition] = useState<number | null>(null)
62
67
  const [code, setCode] = useState(initialCode)
63
68
 
@@ -256,32 +261,33 @@ export const CodeEditor = ({
256
261
  tsSync(),
257
262
  tsLinter(),
258
263
  autocompletion({ override: [tsAutocomplete()] }),
259
- tsHover(),
260
264
  hoverTooltip((view, pos) => {
261
- const line = view.state.doc.lineAt(pos)
262
- const lineStart = line.from
263
- const lineEnd = line.to
264
- const lineText = view.state.sliceDoc(lineStart, lineEnd)
265
- const matches = Array.from(
266
- lineText.matchAll(TSCI_PACKAGE_PATTERN),
267
- )
268
-
269
- for (const match of matches) {
270
- if (match.index !== undefined) {
271
- const start = lineStart + match.index
272
- const end = start + match[0].length
273
- if (pos >= start && pos <= end) {
274
- return {
275
- pos: start,
276
- end: end,
277
- above: true,
278
- create() {
279
- const dom = document.createElement("div")
280
- dom.textContent = "Ctrl/Cmd+Click to open snippet"
281
- return { dom }
282
- },
283
- }
284
- }
265
+ const facet = view.state.facet(tsFacet)
266
+ if (!facet) return null
267
+
268
+ const { env, path } = facet
269
+ const info = env.languageService.getQuickInfoAtPosition(path, pos)
270
+ if (!info) return null
271
+
272
+ const start = info.textSpan.start
273
+ const end = start + info.textSpan.length
274
+ const content = ts.displayPartsToString(info.displayParts || [])
275
+
276
+ const dom = document.createElement("div")
277
+ if (highlighter) {
278
+ dom.innerHTML = highlighter.codeToHtml(content, {
279
+ lang: "typescript",
280
+ themes: {
281
+ light: "github-light",
282
+ dark: "github-dark",
283
+ },
284
+ })
285
+
286
+ return {
287
+ pos: start,
288
+ end,
289
+ above: true,
290
+ create: () => ({ dom }),
285
291
  }
286
292
  }
287
293
  return null
@@ -321,10 +327,21 @@ export const CodeEditor = ({
321
327
  },
322
328
  }),
323
329
  EditorView.theme({
324
- ".cm-content .cm-underline": {
325
- textDecoration: "underline",
326
- textDecorationColor: "rgba(0, 0, 255, 0.3)",
327
- cursor: "pointer",
330
+ ".shiki": {
331
+ maxWidth: "600px",
332
+ padding: "12px",
333
+ maxHeight: "400px",
334
+ borderRadius: "0.5rem",
335
+ backgroundColor: "#fff",
336
+ color: "#0f172a",
337
+ border: "1px solid #e2e8f0",
338
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)",
339
+ fontSize: "14px",
340
+ fontFamily: "monospace",
341
+ whiteSpace: "pre-wrap",
342
+ lineHeight: "1.6",
343
+ overflow: "auto",
344
+ zIndex: "9999",
328
345
  },
329
346
  }),
330
347
  EditorView.decorations.of((view) => {
@@ -48,6 +48,7 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
48
48
  const [showResults, setShowResults] = useState(false)
49
49
  const axios = useAxios()
50
50
  const resultsRef = useRef<HTMLDivElement>(null)
51
+ const inputRef = useRef<HTMLInputElement>(null)
51
52
  const [location] = useLocation()
52
53
  const { snippetsBaseApiUrl } = useSnippetsBaseApiUrl()
53
54
 
@@ -71,6 +72,11 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
71
72
  setShowResults(!!searchQuery)
72
73
  }
73
74
 
75
+ // Focus input on mount
76
+ useEffect(() => {
77
+ inputRef.current?.focus()
78
+ }, [])
79
+
74
80
  useEffect(() => {
75
81
  const handleClickOutside = (event: MouseEvent) => {
76
82
  if (
@@ -93,6 +99,7 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
93
99
  return (
94
100
  <form onSubmit={handleSearch} className="relative">
95
101
  <Input
102
+ ref={inputRef}
96
103
  type="search"
97
104
  placeholder="Search"
98
105
  className="pl-4 focus:border-blue-500 placeholder-gray-400 text-sm"
@@ -6,6 +6,7 @@ import { Skeleton } from "@/components/ui/skeleton"
6
6
  import { usePackageFile, usePackageFileByPath } from "@/hooks/use-package-files"
7
7
  import { ShikiCodeViewer } from "./ShikiCodeViewer"
8
8
  import { SparklesIcon } from "lucide-react"
9
+ import MarkdownViewer from "./markdown-viewer"
9
10
 
10
11
  interface PackageFile {
11
12
  package_file_id: string
@@ -86,13 +87,13 @@ export default function ImportantFilesView({
86
87
  {aiDescription && (
87
88
  <div className="mb-6">
88
89
  <h3 className="font-semibold text-lg mb-2">Description</h3>
89
- <p className="whitespace-pre-wrap">{aiDescription}</p>
90
+ <MarkdownViewer markdownContent={aiDescription} />
90
91
  </div>
91
92
  )}
92
93
  {aiUsageInstructions && (
93
94
  <div>
94
95
  <h3 className="font-semibold text-lg mb-2">Instructions</h3>
95
- <p className="whitespace-pre-wrap">{aiUsageInstructions}</p>
96
+ <MarkdownViewer markdownContent={aiUsageInstructions} />
96
97
  </div>
97
98
  )}
98
99
  </div>
@@ -213,10 +214,7 @@ export default function ImportantFilesView({
213
214
  {activeTab === "ai" ? (
214
215
  renderAiContent()
215
216
  ) : activeFilePath && activeFilePath.endsWith(".md") ? (
216
- <div className="markdown-content">
217
- {/* In a real app, you'd use a markdown renderer here */}
218
- <pre className="whitespace-pre-wrap">{activeFileContent}</pre>
219
- </div>
217
+ <MarkdownViewer markdownContent={activeFileContent} />
220
218
  ) : activeFilePath &&
221
219
  (activeFilePath.endsWith(".js") ||
222
220
  activeFilePath.endsWith(".jsx") ||
@@ -0,0 +1,37 @@
1
+ import Markdown from "react-markdown"
2
+ import remarkGfm from "remark-gfm"
3
+
4
+ export default function MarkdownViewer({
5
+ markdownContent,
6
+ }: {
7
+ markdownContent: string
8
+ }) {
9
+ 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">
22
+ {children}
23
+ </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>
35
+ </div>
36
+ )
37
+ }
@@ -8,9 +8,9 @@ import { Button } from "@/components/ui/button"
8
8
  import { useEditPackageDetailsDialog } from "@/components/dialogs/edit-package-details-dialog"
9
9
  import { useState, useEffect, useMemo } from "react"
10
10
  import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
11
- import { PackageInfo } from "./sidebar-about-section"
12
11
  import { usePackageFile } from "@/hooks/use-package-files"
13
12
  import { getLicenseFromLicenseContent } from "@/lib/getLicenseFromLicenseContent"
13
+ import { PackageInfo } from "@/lib/types"
14
14
 
15
15
  interface MobileSidebarProps {
16
16
  isLoading?: boolean