@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
@@ -28,7 +28,7 @@ export default withRouteSpec({
28
28
  package_name_with_version,
29
29
  } = req.jsonBody
30
30
 
31
- if (package_name_with_version && !version && !package_id) {
31
+ if (package_name_with_version && !package_id) {
32
32
  const [packageName, parsedVersion] = package_name_with_version.split("@")
33
33
  const pkg = ctx.db.packages.find((p) => p.name === packageName)
34
34
 
@@ -0,0 +1,128 @@
1
+ import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
2
+ import { z } from "zod"
3
+
4
+ const PROXY_HEADERS = [
5
+ "X-Target-Url",
6
+ "X-Sender-Origin",
7
+ "X-Sender-Host",
8
+ "X-Sender-Referer",
9
+ "X-Sender-User-Agent",
10
+ "X-Sender-Cookie",
11
+ ]
12
+
13
+ const ALLOWED_DOMAINS: Array<{ domain: string; routes?: string[] }> = [
14
+ { domain: "easyeda.com" },
15
+ ]
16
+
17
+ function isAllowedDomain(url: string) {
18
+ try {
19
+ const { hostname, pathname, port } = new URL(url)
20
+ const allowedDomain = ALLOWED_DOMAINS.find(
21
+ (domain) => domain.domain === hostname,
22
+ )
23
+
24
+ if (allowedDomain) {
25
+ if (
26
+ !allowedDomain.routes ||
27
+ allowedDomain.routes.some((route) => pathname.startsWith(route))
28
+ ) {
29
+ return true
30
+ }
31
+ }
32
+
33
+ return hostname === "localhost" || (hostname === "127.0.0.1" && port)
34
+ } catch {
35
+ return false
36
+ }
37
+ }
38
+
39
+ export default withRouteSpec({
40
+ methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
41
+ jsonResponse: z.any(),
42
+ auth: "session",
43
+ })(async (req, ctx) => {
44
+ if (req.headers.get("X-proxied")) {
45
+ return ctx.json(
46
+ { error: "Recursive proxy calls are not allowed" },
47
+ { status: 403 },
48
+ )
49
+ }
50
+
51
+ const targetUrl = req.headers.get("X-Target-Url")
52
+
53
+ if (!targetUrl) {
54
+ return ctx.json(
55
+ { error: "X-Target-Url header is required" },
56
+ { status: 400 },
57
+ )
58
+ }
59
+
60
+ if (!isAllowedDomain(targetUrl)) {
61
+ return ctx.json({ error: "Domain not allowed" }, { status: 403 })
62
+ }
63
+
64
+ let body: string | undefined
65
+ if (["POST", "PUT", "PATCH"].includes(req.method)) {
66
+ body = await req.clone().text()
67
+ }
68
+
69
+ const headers = new Headers(req.headers)
70
+
71
+ const senderOrigin = req.headers.get("X-Sender-Origin")
72
+ if (senderOrigin) {
73
+ headers.set("Origin", senderOrigin)
74
+ }
75
+
76
+ const senderHost = req.headers.get("X-Sender-Host")
77
+ if (senderHost) {
78
+ const hostValue = senderHost.replace(/^https?:\/\//, "")
79
+ headers.set("Host", hostValue)
80
+ headers.set("authority", hostValue)
81
+ }
82
+
83
+ const senderReferer = req.headers.get("X-Sender-Referer")
84
+ if (senderReferer) {
85
+ headers.set("Referer", senderReferer)
86
+ }
87
+
88
+ const senderUserAgent = req.headers.get("X-Sender-User-Agent")
89
+ if (senderUserAgent) {
90
+ headers.set("User-Agent", senderUserAgent)
91
+ }
92
+
93
+ const senderCookie = req.headers.get("X-Sender-Cookie")
94
+ if (senderCookie) {
95
+ headers.set("Cookie", senderCookie)
96
+ }
97
+
98
+ for (const header of PROXY_HEADERS) {
99
+ headers.delete(header)
100
+ }
101
+
102
+ headers.delete("content-encoding")
103
+ headers.delete("accept-encoding")
104
+ headers.set("X-proxied", "true")
105
+
106
+ try {
107
+ const response = await fetch(targetUrl, {
108
+ method: req.method,
109
+ headers: headers,
110
+ body: ["GET", "HEAD"].includes(req.method) ? undefined : body,
111
+ })
112
+
113
+ const responseHeaders = new Headers(response.headers)
114
+ responseHeaders.delete("content-encoding")
115
+
116
+ return new Response(response.body, {
117
+ status: response.status,
118
+ statusText: response.statusText,
119
+ headers: responseHeaders,
120
+ })
121
+ } catch (error) {
122
+ console.error("Proxy error:", error)
123
+ return ctx.json(
124
+ { error: { message: "Failed to proxy request" } },
125
+ { status: 502 },
126
+ )
127
+ }
128
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.87",
3
+ "version": "0.0.89",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,9 +32,12 @@
32
32
  "generate-images": "bun run scripts/generate-image-sizes.ts",
33
33
  "generate-sitemap": "bun run scripts/generate-sitemap.ts"
34
34
  },
35
- "dependencies": {
35
+ "devDependencies": {
36
+ "@anthropic-ai/sdk": "^0.27.3",
36
37
  "@babel/preset-react": "^7.25.9",
37
38
  "@babel/preset-typescript": "^7.26.0",
39
+ "@babel/standalone": "^7.26.2",
40
+ "@biomejs/biome": "^1.9.2",
38
41
  "@codemirror/autocomplete": "^6.18.1",
39
42
  "@codemirror/lang-javascript": "^6.2.2",
40
43
  "@codemirror/lang-json": "^6.0.1",
@@ -42,6 +45,7 @@
42
45
  "@codemirror/state": "^6.4.1",
43
46
  "@codemirror/view": "^6.34.1",
44
47
  "@hookform/resolvers": "^3.9.0",
48
+ "@playwright/test": "^1.48.0",
45
49
  "@radix-ui/react-accordion": "^1.2.4",
46
50
  "@radix-ui/react-alert-dialog": "^1.1.1",
47
51
  "@radix-ui/react-aspect-ratio": "^1.1.0",
@@ -70,24 +74,48 @@
70
74
  "@radix-ui/react-toggle": "^1.1.0",
71
75
  "@radix-ui/react-toggle-group": "^1.1.0",
72
76
  "@radix-ui/react-tooltip": "^1.1.2",
73
- "@tscircuit/footprinter": "^0.0.176",
77
+ "@tailwindcss/typography": "^0.5.16",
78
+ "@tscircuit/3d-viewer": "^0.0.279",
79
+ "@tscircuit/assembly-viewer": "^0.0.1",
80
+ "@tscircuit/core": "^0.0.536",
81
+ "@tscircuit/create-snippet-url": "^0.0.8",
82
+ "@tscircuit/eval": "^0.0.244",
83
+ "@tscircuit/footprinter": "^0.0.186",
74
84
  "@tscircuit/layout": "^0.0.29",
75
85
  "@tscircuit/math-utils": "^0.0.10",
76
86
  "@tscircuit/mm": "^0.0.8",
77
- "@tscircuit/props": "^0.0.194",
87
+ "@tscircuit/pcb-viewer": "^1.11.194",
88
+ "@tscircuit/prompt-benchmarks": "^0.0.28",
89
+ "@tscircuit/props": "^0.0.246",
90
+ "@tscircuit/runframe": "^0.0.653",
91
+ "@tscircuit/schematic-viewer": "^2.0.21",
92
+ "@types/babel__standalone": "^7.1.7",
93
+ "@types/bun": "^1.1.10",
94
+ "@types/country-list": "^2.1.4",
78
95
  "@types/file-saver": "^2.0.7",
96
+ "@types/md5": "^2.3.5",
79
97
  "@types/ms": "^0.7.34",
98
+ "@types/node": "^22.13.0",
99
+ "@types/prismjs": "^1.26.4",
100
+ "@types/react": "^18.3.9",
101
+ "@types/react-dom": "^18.3.0",
102
+ "@types/react-helmet": "^6.1.11",
103
+ "@types/sharp": "^0.32.0",
80
104
  "@typescript/ata": "^0.9.7",
105
+ "@typescript/vfs": "^1.6.0",
81
106
  "@valtown/codemirror-codeium": "^1.1.1",
82
107
  "@valtown/codemirror-ts": "^2.2.0",
83
108
  "@vercel/analytics": "^1.4.1",
109
+ "@vitejs/plugin-react": "^4.3.1",
110
+ "autoprefixer": "^10.4.20",
84
111
  "change-case": "^5.4.4",
85
112
  "circuit-json": "^0.0.190",
86
- "circuit-json-to-bom-csv": "^0.0.6",
87
- "circuit-json-to-gerber": "^0.0.21",
88
- "circuit-json-to-pnp-csv": "^0.0.6",
113
+ "circuit-json-to-bom-csv": "^0.0.7",
114
+ "circuit-json-to-gerber": "^0.0.25",
115
+ "circuit-json-to-pnp-csv": "^0.0.7",
89
116
  "circuit-json-to-readable-netlist": "^0.0.13",
90
117
  "circuit-json-to-tscircuit": "^0.0.4",
118
+ "circuit-to-svg": "^0.0.163",
91
119
  "class-variance-authority": "^0.7.1",
92
120
  "clsx": "^2.1.1",
93
121
  "cmdk": "^1.0.4",
@@ -95,11 +123,15 @@
95
123
  "country-list": "^2.3.0",
96
124
  "date-fns": "^4.1.0",
97
125
  "dsn-converter": "^0.0.60",
98
- "easyeda": "^0.0.129",
126
+ "easyeda": "^0.0.195",
99
127
  "embla-carousel-react": "^8.3.0",
100
128
  "extract-codefence": "^0.0.4",
101
129
  "fflate": "^0.8.2",
102
130
  "file-saver": "^2.0.5",
131
+ "get-port": "^7.1.0",
132
+ "globals": "^15.9.0",
133
+ "he": "^1.2.0",
134
+ "idb-keyval": "^6.2.2",
103
135
  "immer": "^10.1.1",
104
136
  "input-otp": "^1.2.4",
105
137
  "javascript-time-ago": "^2.5.11",
@@ -107,11 +139,17 @@
107
139
  "jscad-electronics": "^0.0.25",
108
140
  "jszip": "^3.10.1",
109
141
  "kicad-converter": "^0.0.16",
142
+ "ky": "^1.7.5",
110
143
  "lucide-react": "^0.488.0",
144
+ "lz-string": "^1.5.0",
111
145
  "md5": "^2.3.0",
112
146
  "ms": "^2.1.3",
113
147
  "next-themes": "^0.3.0",
148
+ "postcss": "^8.4.47",
114
149
  "posthog-js": "^1.203.2",
150
+ "prismjs": "^1.29.0",
151
+ "prompts": "^2.4.2",
152
+ "react": "^18.3.1",
115
153
  "react-cookie-consent": "^9.0.0",
116
154
  "react-day-picker": "8.10.1",
117
155
  "react-dom": "^18.3.1",
@@ -124,62 +162,31 @@
124
162
  "react-query": "^3.39.3",
125
163
  "react-resizable-panels": "^2.1.3",
126
164
  "recharts": "^2.12.7",
165
+ "redaxios": "^0.5.1",
127
166
  "remark-gfm": "^4.0.1",
128
167
  "rollup-plugin-visualizer": "^5.12.0",
129
168
  "schematic-symbols": "^0.0.155",
169
+ "sharp": "^0.33.5",
170
+ "shiki": "^3.2.1",
130
171
  "sitemap": "^8.0.0",
131
172
  "sonner": "^1.5.0",
132
173
  "states-us": "^1.1.1",
133
174
  "tailwind-merge": "^2.5.2",
134
- "tailwindcss-animate": "^1.0.7",
135
- "use-async-memo": "^1.2.5",
136
- "use-mouse-matrix-transform": "^1.3.0",
137
- "vaul": "^0.9.9",
138
- "vite-plugin-vercel": "^9.0.4",
139
- "wouter": "^3.3.5"
140
- },
141
- "devDependencies": {
142
- "@anthropic-ai/sdk": "^0.27.3",
143
- "@babel/standalone": "^7.26.2",
144
- "@biomejs/biome": "^1.9.2",
145
- "@playwright/test": "^1.48.0",
146
- "@tailwindcss/typography": "^0.5.16",
147
- "@tscircuit/core": "^0.0.433",
148
- "@tscircuit/eval": "^0.0.227",
149
- "@tscircuit/prompt-benchmarks": "^0.0.28",
150
- "@tscircuit/runframe": "^0.0.578",
151
- "@types/babel__standalone": "^7.1.7",
152
- "@types/bun": "^1.1.10",
153
- "@types/country-list": "^2.1.4",
154
- "@types/md5": "^2.3.5",
155
- "@types/node": "^22.13.0",
156
- "@types/prismjs": "^1.26.4",
157
- "@types/react": "^18.3.9",
158
- "@types/react-dom": "^18.3.0",
159
- "@types/react-helmet": "^6.1.11",
160
- "@types/sharp": "^0.32.0",
161
- "@typescript/vfs": "^1.6.0",
162
- "@vitejs/plugin-react": "^4.3.1",
163
- "autoprefixer": "^10.4.20",
164
- "circuit-to-svg": "^0.0.152",
165
- "get-port": "^7.1.0",
166
- "globals": "^15.9.0",
167
- "he": "^1.2.0",
168
- "ky": "^1.7.5",
169
- "postcss": "^8.4.47",
170
- "prismjs": "^1.29.0",
171
- "prompts": "^2.4.2",
172
- "react": "^18.3.1",
173
- "redaxios": "^0.5.1",
174
- "sharp": "^0.33.5",
175
- "shiki": "^3.2.1",
176
175
  "tailwindcss": "^3.4.13",
176
+ "tailwindcss-animate": "^1.0.7",
177
177
  "terser": "^5.27.0",
178
+ "three": "^0.177.0",
179
+ "three-stdlib": "^2.36.0",
178
180
  "tsup": "^8.5.0",
179
181
  "typescript": "^5.6.3",
182
+ "use-async-memo": "^1.2.5",
183
+ "use-mouse-matrix-transform": "^1.3.0",
184
+ "vaul": "^0.9.9",
180
185
  "vite": "^6.3.4",
181
186
  "vite-plugin-image-optimizer": "^1.1.8",
187
+ "vite-plugin-vercel": "^9.0.4",
182
188
  "winterspec": "^0.0.107",
189
+ "wouter": "^3.3.5",
183
190
  "zod": "^3.23.8",
184
191
  "zustand": "^4.5.5",
185
192
  "zustand-hoist": "^2.0.1"
package/renovate.json CHANGED
@@ -10,7 +10,8 @@
10
10
  "jscad-electronics",
11
11
  "circuit-json",
12
12
  "dsn-converter",
13
- "circuit-json-to-readable-netlist"
13
+ "circuit-json-to-readable-netlist",
14
+ "circuit-*"
14
15
  ],
15
16
  "enabled": false
16
17
  },
package/src/App.tsx CHANGED
@@ -78,18 +78,37 @@ const PackageEditorPage = lazyImport(async () => {
78
78
 
79
79
  class ErrorBoundary extends React.Component<
80
80
  { children: React.ReactNode },
81
- { hasError: boolean }
81
+ { hasError: boolean; reloading: boolean }
82
82
  > {
83
83
  constructor(props: { children: React.ReactNode }) {
84
84
  super(props)
85
- this.state = { hasError: false }
85
+ this.state = { hasError: false, reloading: false }
86
86
  }
87
87
 
88
88
  static getDerivedStateFromError() {
89
- return { hasError: true }
89
+ return { hasError: true, reloading: false }
90
+ }
91
+
92
+ componentDidCatch(error: Error) {
93
+ console.error("ErrorBoundary caught", error)
94
+ const message = error.message || ""
95
+ if (
96
+ /(Loading chunk|ChunkLoadError|dynamically imported module)/i.test(
97
+ message,
98
+ )
99
+ ) {
100
+ const loadedAt = window.__APP_LOADED_AT || Date.now()
101
+ if (Date.now() - loadedAt >= 10_000) {
102
+ this.setState({ reloading: true })
103
+ window.location.reload()
104
+ }
105
+ }
90
106
  }
91
107
 
92
108
  render() {
109
+ if (this.state.reloading) {
110
+ return <div>There was a problem loading this page. Reloading…</div>
111
+ }
93
112
  if (this.state.hasError) {
94
113
  return <div>Something went wrong loading the page.</div>
95
114
  }
@@ -4,6 +4,7 @@ import { useEffect } from "react"
4
4
  import { useGlobalStore } from "./hooks/use-global-store"
5
5
  import { posthog } from "./lib/posthog"
6
6
  import { Toaster } from "react-hot-toast"
7
+ import { Toaster as SonnerToaster } from "@/components/ui/sonner"
7
8
  import { populateQueryCacheWithSSRData } from "./lib/populate-query-cache-with-ssr-data"
8
9
 
9
10
  const staffGithubUsernames = [
@@ -65,6 +66,7 @@ export const ContextProviders = ({ children }: any) => {
65
66
  <PostHogIdentifier />
66
67
  {children}
67
68
  <Toaster position="bottom-right" />
69
+ <SonnerToaster position="bottom-left" />
68
70
  </HelmetProvider>
69
71
  </QueryClientProvider>
70
72
  )
@@ -0,0 +1,52 @@
1
+ import { toast } from "sonner"
2
+
3
+ export function setupBuildWatcher() {
4
+ const meta = document.querySelector<HTMLMetaElement>(
5
+ 'meta[name="tscircuit-build"]',
6
+ )
7
+ const currentId = meta?.content || ""
8
+ ;(window as any).TSC_BUILD_ID = currentId
9
+
10
+ async function fetchBuildId(): Promise<string | null> {
11
+ try {
12
+ const res = await fetch("/api/generated-index", { cache: "no-store" })
13
+ const text = await res.text()
14
+ const match = text.match(
15
+ /<meta name="tscircuit-build" content="([^"]+)"/i,
16
+ )
17
+ return match ? match[1] : null
18
+ } catch (err) {
19
+ console.error("Failed to fetch build identifier", err)
20
+ return null
21
+ }
22
+ }
23
+
24
+ let updateToastShown = false
25
+
26
+ async function checkForUpdate() {
27
+ const serverId = await fetchBuildId()
28
+ if (
29
+ serverId &&
30
+ serverId !== (window as any).TSC_BUILD_ID &&
31
+ !updateToastShown
32
+ ) {
33
+ updateToastShown = true
34
+ toast("A new version of tscircuit.com is available.", {
35
+ action: {
36
+ label: "Reload",
37
+ onClick: () => window.location.reload(),
38
+ },
39
+ duration: Infinity,
40
+ position: "bottom-left",
41
+ })
42
+ }
43
+ }
44
+
45
+ document.addEventListener("visibilitychange", () => {
46
+ if (document.visibilityState === "visible") {
47
+ checkForUpdate()
48
+ }
49
+ })
50
+
51
+ setInterval(checkForUpdate, 5 * 60 * 1000)
52
+ }