@tscircuit/fake-snippets 0.0.88 → 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.
- package/api/generated-index.js +96 -14
- package/bun-tests/fake-snippets-api/routes/proxy.test.ts +42 -0
- package/bun.lock +187 -206
- package/dist/bundle.js +206 -100
- package/fake-snippets-api/routes/api/proxy.ts +128 -0
- package/package.json +56 -47
- package/renovate.json +2 -1
- package/src/App.tsx +22 -3
- package/src/ContextProviders.tsx +2 -0
- package/src/build-watcher.ts +52 -0
- package/src/components/CmdKMenu.tsx +533 -197
- package/src/components/DownloadButtonAndMenu.tsx +104 -26
- package/src/components/FileSidebar.tsx +11 -1
- package/src/components/Header2.tsx +7 -2
- package/src/components/PackageBuildsPage/LogContent.tsx +25 -22
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +6 -6
- package/src/components/PackageBuildsPage/build-preview-content.tsx +5 -5
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +15 -13
- package/src/components/PackageBuildsPage/package-build-header.tsx +17 -16
- package/src/components/PackageCard.tsx +66 -16
- package/src/components/SearchComponent.tsx +2 -2
- package/src/components/SuspenseRunFrame.tsx +14 -2
- package/src/components/ViewPackagePage/components/important-files-view.tsx +90 -17
- package/src/components/ViewPackagePage/components/main-content-header.tsx +26 -2
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +2 -2
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +35 -30
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +2 -2
- package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +20 -12
- package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +0 -7
- package/src/components/ViewPackagePage/utils/fuzz-search.ts +121 -0
- package/src/components/ViewPackagePage/utils/is-hidden-file.ts +4 -0
- package/src/components/dialogs/confirm-delete-package-dialog.tsx +1 -1
- package/src/components/dialogs/confirm-discard-changes-dialog.tsx +73 -0
- package/src/components/dialogs/edit-package-details-dialog.tsx +2 -2
- package/src/components/dialogs/view-ts-files-dialog.tsx +478 -42
- package/src/components/package-port/CodeAndPreview.tsx +16 -0
- package/src/components/package-port/CodeEditor.tsx +113 -11
- package/src/components/package-port/CodeEditorHeader.tsx +39 -4
- package/src/components/package-port/EditorNav.tsx +41 -15
- package/src/components/package-port/GlobalFindReplace.tsx +681 -0
- package/src/components/package-port/QuickOpen.tsx +241 -0
- package/src/components/ui/dialog.tsx +1 -1
- package/src/components/ui/tree-view.tsx +1 -1
- package/src/global.d.ts +3 -0
- package/src/hooks/use-ai-review.ts +31 -0
- package/src/hooks/use-current-package-release.ts +5 -1
- package/src/hooks/use-download-zip.ts +50 -0
- package/src/hooks/use-hotkey.ts +116 -0
- package/src/hooks/use-package-by-package-id.ts +1 -0
- package/src/hooks/use-package-by-package-name.ts +1 -0
- package/src/hooks/use-package-files.ts +3 -0
- package/src/hooks/use-package-release.ts +5 -1
- package/src/hooks/use-package.ts +1 -0
- package/src/hooks/use-request-ai-review-mutation.ts +14 -6
- package/src/hooks/use-snippet.ts +1 -0
- package/src/hooks/useFileManagement.ts +26 -8
- package/src/hooks/usePackageFilesLoader.ts +3 -1
- package/src/index.css +11 -0
- package/src/lib/decodeUrlHashToFsMap.ts +17 -0
- package/src/lib/download-fns/download-circuit-png.ts +88 -0
- package/src/lib/download-fns/download-png-utils.ts +31 -0
- package/src/lib/encodeFsMapToUrlHash.ts +13 -0
- package/src/lib/populate-query-cache-with-ssr-data.ts +7 -0
- package/src/lib/ts-lib-cache.ts +47 -0
- package/src/lib/types.ts +2 -0
- package/src/main.tsx +7 -0
- package/src/pages/dashboard.tsx +8 -5
- package/src/pages/view-package.tsx +15 -7
- package/vite.config.ts +100 -1
|
@@ -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.
|
|
3
|
+
"version": "0.0.89",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,8 +33,11 @@
|
|
|
33
33
|
"generate-sitemap": "bun run scripts/generate-sitemap.ts"
|
|
34
34
|
},
|
|
35
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
|
-
"@
|
|
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/
|
|
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.
|
|
87
|
-
"circuit-json-to-gerber": "^0.0.
|
|
88
|
-
"circuit-json-to-pnp-csv": "^0.0.
|
|
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.
|
|
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,60 +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
|
-
"@anthropic-ai/sdk": "^0.27.3",
|
|
141
|
-
"@babel/standalone": "^7.26.2",
|
|
142
|
-
"@biomejs/biome": "^1.9.2",
|
|
143
|
-
"@playwright/test": "^1.48.0",
|
|
144
|
-
"@tailwindcss/typography": "^0.5.16",
|
|
145
|
-
"@tscircuit/core": "^0.0.433",
|
|
146
|
-
"@tscircuit/eval": "^0.0.227",
|
|
147
|
-
"@tscircuit/prompt-benchmarks": "^0.0.28",
|
|
148
|
-
"@tscircuit/runframe": "^0.0.582",
|
|
149
|
-
"@types/babel__standalone": "^7.1.7",
|
|
150
|
-
"@types/bun": "^1.1.10",
|
|
151
|
-
"@types/country-list": "^2.1.4",
|
|
152
|
-
"@types/md5": "^2.3.5",
|
|
153
|
-
"@types/node": "^22.13.0",
|
|
154
|
-
"@types/prismjs": "^1.26.4",
|
|
155
|
-
"@types/react": "^18.3.9",
|
|
156
|
-
"@types/react-dom": "^18.3.0",
|
|
157
|
-
"@types/react-helmet": "^6.1.11",
|
|
158
|
-
"@types/sharp": "^0.32.0",
|
|
159
|
-
"@typescript/vfs": "^1.6.0",
|
|
160
|
-
"@vitejs/plugin-react": "^4.3.1",
|
|
161
|
-
"autoprefixer": "^10.4.20",
|
|
162
|
-
"circuit-to-svg": "^0.0.152",
|
|
163
|
-
"get-port": "^7.1.0",
|
|
164
|
-
"globals": "^15.9.0",
|
|
165
|
-
"he": "^1.2.0",
|
|
166
|
-
"ky": "^1.7.5",
|
|
167
|
-
"postcss": "^8.4.47",
|
|
168
|
-
"prismjs": "^1.29.0",
|
|
169
|
-
"prompts": "^2.4.2",
|
|
170
|
-
"react": "^18.3.1",
|
|
171
|
-
"redaxios": "^0.5.1",
|
|
172
|
-
"sharp": "^0.33.5",
|
|
173
|
-
"shiki": "^3.2.1",
|
|
174
175
|
"tailwindcss": "^3.4.13",
|
|
176
|
+
"tailwindcss-animate": "^1.0.7",
|
|
175
177
|
"terser": "^5.27.0",
|
|
178
|
+
"three": "^0.177.0",
|
|
179
|
+
"three-stdlib": "^2.36.0",
|
|
176
180
|
"tsup": "^8.5.0",
|
|
177
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",
|
|
178
185
|
"vite": "^6.3.4",
|
|
179
186
|
"vite-plugin-image-optimizer": "^1.1.8",
|
|
187
|
+
"vite-plugin-vercel": "^9.0.4",
|
|
180
188
|
"winterspec": "^0.0.107",
|
|
189
|
+
"wouter": "^3.3.5",
|
|
181
190
|
"zod": "^3.23.8",
|
|
182
191
|
"zustand": "^4.5.5",
|
|
183
192
|
"zustand-hoist": "^2.0.1"
|
package/renovate.json
CHANGED
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
|
}
|
package/src/ContextProviders.tsx
CHANGED
|
@@ -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
|
+
}
|