@tscircuit/fake-snippets 0.0.6 → 0.0.8
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/fixtures/get-test-server.ts +2 -5
- 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-tests/fake-snippets-api/routes/packages/{list.test.ts → list-1.test.ts} +3 -55
- package/bun-tests/fake-snippets-api/routes/packages/list-2.test.ts +59 -0
- package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +34 -1
- package/bun.lock +100 -73
- package/dist/bundle.js +729 -273
- package/fake-snippets-api/lib/db/db-client.ts +58 -50
- package/fake-snippets-api/lib/db/schema.ts +15 -6
- 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/lib/public-mapping/public-map-package.ts +9 -2
- 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/fake-snippets-api/routes/api/snippets/create.ts +123 -29
- package/index.html +12 -1
- package/package.json +10 -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/index.html
CHANGED
|
@@ -84,6 +84,17 @@
|
|
|
84
84
|
}, 300); // Match the opacity transition duration
|
|
85
85
|
};
|
|
86
86
|
</script>
|
|
87
|
+
|
|
88
|
+
<!-- Google tag (gtag.js) -->
|
|
89
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-16607843883">
|
|
90
|
+
</script>
|
|
91
|
+
<script>
|
|
92
|
+
window.dataLayer = window.dataLayer || [];
|
|
93
|
+
function gtag(){dataLayer.push(arguments);}
|
|
94
|
+
gtag('js', new Date());
|
|
95
|
+
|
|
96
|
+
gtag('config', 'AW-16607843883');
|
|
97
|
+
</script>
|
|
87
98
|
</head>
|
|
88
99
|
|
|
89
100
|
<body>
|
|
@@ -102,4 +113,4 @@
|
|
|
102
113
|
<script type="module" src="./src/main.tsx"></script>
|
|
103
114
|
</body>
|
|
104
115
|
|
|
105
|
-
</html>
|
|
116
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/fake-snippets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -61,13 +61,13 @@
|
|
|
61
61
|
"@radix-ui/react-toggle": "^1.1.0",
|
|
62
62
|
"@radix-ui/react-toggle-group": "^1.1.0",
|
|
63
63
|
"@radix-ui/react-tooltip": "^1.1.2",
|
|
64
|
-
"@tscircuit/3d-viewer": "^0.0.
|
|
65
|
-
"@tscircuit/footprinter": "^0.0.
|
|
64
|
+
"@tscircuit/3d-viewer": "^0.0.142",
|
|
65
|
+
"@tscircuit/footprinter": "^0.0.124",
|
|
66
66
|
"@tscircuit/layout": "^0.0.29",
|
|
67
67
|
"@tscircuit/math-utils": "^0.0.10",
|
|
68
68
|
"@tscircuit/mm": "^0.0.8",
|
|
69
69
|
"@tscircuit/pcb-viewer": "^1.11.12",
|
|
70
|
-
"@tscircuit/props": "^0.0.
|
|
70
|
+
"@tscircuit/props": "^0.0.143",
|
|
71
71
|
"@tscircuit/schematic-viewer": "^1.4.3",
|
|
72
72
|
"@types/file-saver": "^2.0.7",
|
|
73
73
|
"@types/ms": "^0.7.34",
|
|
@@ -76,12 +76,12 @@
|
|
|
76
76
|
"@valtown/codemirror-ts": "^2.2.0",
|
|
77
77
|
"@vercel/analytics": "^1.4.1",
|
|
78
78
|
"change-case": "^5.4.4",
|
|
79
|
-
"circuit-json": "^0.0.
|
|
79
|
+
"circuit-json": "^0.0.136",
|
|
80
80
|
"circuit-json-to-bom-csv": "^0.0.6",
|
|
81
81
|
"circuit-json-to-gerber": "^0.0.16",
|
|
82
82
|
"circuit-json-to-pnp-csv": "^0.0.6",
|
|
83
|
+
"circuit-json-to-readable-netlist": "^0.0.8",
|
|
83
84
|
"circuit-json-to-tscircuit": "^0.0.4",
|
|
84
|
-
"circuit-json-to-readable-netlist": "^0.0.7",
|
|
85
85
|
"class-variance-authority": "^0.7.0",
|
|
86
86
|
"clsx": "^2.1.1",
|
|
87
87
|
"cmdk": "^1.0.4",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"immer": "^10.1.1",
|
|
98
98
|
"input-otp": "^1.2.4",
|
|
99
99
|
"jose": "^5.9.3",
|
|
100
|
-
"jscad-electronics": "^0.0.
|
|
100
|
+
"jscad-electronics": "^0.0.25",
|
|
101
101
|
"jszip": "^3.10.1",
|
|
102
102
|
"kicad-converter": "^0.0.16",
|
|
103
103
|
"lucide-react": "^0.445.0",
|
|
@@ -130,9 +130,9 @@
|
|
|
130
130
|
"@babel/standalone": "^7.26.2",
|
|
131
131
|
"@biomejs/biome": "^1.9.2",
|
|
132
132
|
"@playwright/test": "^1.48.0",
|
|
133
|
-
"@tscircuit/core": "^0.0.
|
|
133
|
+
"@tscircuit/core": "^0.0.315",
|
|
134
134
|
"@tscircuit/prompt-benchmarks": "^0.0.28",
|
|
135
|
-
"@tscircuit/runframe": "^0.0.
|
|
135
|
+
"@tscircuit/runframe": "^0.0.193",
|
|
136
136
|
"@types/babel__standalone": "^7.1.7",
|
|
137
137
|
"@types/bun": "^1.1.10",
|
|
138
138
|
"@types/country-list": "^2.1.4",
|
|
@@ -146,6 +146,7 @@
|
|
|
146
146
|
"@vitejs/plugin-react": "^4.3.1",
|
|
147
147
|
"autoprefixer": "^10.4.20",
|
|
148
148
|
"circuit-to-svg": "^0.0.101",
|
|
149
|
+
"get-port": "^7.1.0",
|
|
149
150
|
"globals": "^15.9.0",
|
|
150
151
|
"postcss": "^8.4.47",
|
|
151
152
|
"prismjs": "^1.29.0",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test"
|
|
2
|
+
|
|
3
|
+
test("test delete functionality in profile", async ({ page }) => {
|
|
4
|
+
// Go to profile page
|
|
5
|
+
await page.goto("http://localhost:5177/testuser")
|
|
6
|
+
|
|
7
|
+
// Login
|
|
8
|
+
await page.getByRole("button", { name: "Log in" }).click()
|
|
9
|
+
|
|
10
|
+
// Wait for snippets grid to load
|
|
11
|
+
await page.waitForSelector(".grid")
|
|
12
|
+
await page.waitForLoadState("networkidle")
|
|
13
|
+
|
|
14
|
+
// Verify initial snippet exists
|
|
15
|
+
const snippetTitle = page.locator(".text-md.font-semibold").first()
|
|
16
|
+
const snippetName = await snippetTitle.textContent()
|
|
17
|
+
expect(await snippetTitle.isVisible()).toBe(true)
|
|
18
|
+
|
|
19
|
+
// Take screenshot of initial state
|
|
20
|
+
await expect(page).toHaveScreenshot("profile-page-before-delete.png")
|
|
21
|
+
|
|
22
|
+
// Open dropdown menu
|
|
23
|
+
await page.locator(".lucide-ellipsis-vertical").first().click()
|
|
24
|
+
await page.waitForTimeout(1000)
|
|
25
|
+
|
|
26
|
+
// Take screenshot with dropdown open
|
|
27
|
+
await expect(page).toHaveScreenshot("profile-page-dropdown-open.png")
|
|
28
|
+
|
|
29
|
+
// Click delete option
|
|
30
|
+
await page.getByRole("menuitem", { name: "Delete Snippet" }).click()
|
|
31
|
+
|
|
32
|
+
// Wait for and verify confirmation dialog
|
|
33
|
+
const dialog = page.locator('[role="dialog"]')
|
|
34
|
+
await expect(dialog).toBeVisible()
|
|
35
|
+
const dialogText = await dialog.textContent()
|
|
36
|
+
expect(dialogText).toContain(
|
|
37
|
+
`Are you sure you want to delete the snippet "${snippetName}"?`,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
// Take screenshot of delete dialog
|
|
41
|
+
await expect(page).toHaveScreenshot("profile-page-delete-dialog.png")
|
|
42
|
+
|
|
43
|
+
// Confirm delete
|
|
44
|
+
await page.getByRole("button", { name: "Delete" }).click()
|
|
45
|
+
|
|
46
|
+
// Verify success toast appears
|
|
47
|
+
await page.waitForSelector('div:has-text("Successfully deleted")', {
|
|
48
|
+
state: "visible",
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Wait for page to update
|
|
52
|
+
await page.waitForLoadState("networkidle")
|
|
53
|
+
|
|
54
|
+
// Verify snippet is removed
|
|
55
|
+
const remainingSnippets = await page
|
|
56
|
+
.locator(`.text-md.font-semibold:has-text("${snippetName}")`)
|
|
57
|
+
.count()
|
|
58
|
+
expect(remainingSnippets).toBe(0)
|
|
59
|
+
})
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -82,11 +82,22 @@ function compareSizes(prData, mainData, dependencies) {
|
|
|
82
82
|
const mainSize = mainStats.depStats[dep]?.size || 0
|
|
83
83
|
const diff = prSize - mainSize
|
|
84
84
|
|
|
85
|
+
let percentChange = "N/A"
|
|
86
|
+
if (prSize === 0 && mainSize > 0) {
|
|
87
|
+
percentChange = "Removed"
|
|
88
|
+
} else if (mainSize !== 0) {
|
|
89
|
+
percentChange = (diff / mainSize) * 100
|
|
90
|
+
} else if (prSize > 0 && mainSize === 0) {
|
|
91
|
+
percentChange = "Added"
|
|
92
|
+
} else {
|
|
93
|
+
percentChange = 0
|
|
94
|
+
}
|
|
95
|
+
|
|
85
96
|
diffStats[dep] = {
|
|
86
97
|
before: mainSize,
|
|
87
98
|
after: prSize,
|
|
88
99
|
diff,
|
|
89
|
-
percentChange
|
|
100
|
+
percentChange,
|
|
90
101
|
}
|
|
91
102
|
})
|
|
92
103
|
|
|
@@ -95,7 +106,10 @@ function compareSizes(prData, mainData, dependencies) {
|
|
|
95
106
|
totalAfter: prStats.totalSize,
|
|
96
107
|
totalDiff: prStats.totalSize - mainStats.totalSize,
|
|
97
108
|
totalPercentChange:
|
|
98
|
-
|
|
109
|
+
mainStats.totalSize !== 0
|
|
110
|
+
? ((prStats.totalSize - mainStats.totalSize) / mainStats.totalSize) *
|
|
111
|
+
100
|
|
112
|
+
: 0,
|
|
99
113
|
diffStats,
|
|
100
114
|
}
|
|
101
115
|
}
|
|
@@ -107,7 +121,7 @@ function generateDiffMarkdown(prData, mainData, dependencies) {
|
|
|
107
121
|
markdown += `## Total Bundle Size\n\n`
|
|
108
122
|
markdown += `- Before: **${formatBytes(comparison.totalBefore)}**\n`
|
|
109
123
|
markdown += `- After: **${formatBytes(comparison.totalAfter)}**\n`
|
|
110
|
-
markdown += `- Change: ${totalDiffSymbol} **${formatBytes(Math.abs(comparison.totalDiff))}** (${comparison.totalPercentChange.toFixed(2)}%)\n\n`
|
|
124
|
+
markdown += `- Change: ${totalDiffSymbol} **${formatBytes(Math.abs(comparison.totalDiff))}** (${isNaN(comparison.totalPercentChange) ? "N/A" : comparison.totalPercentChange.toFixed(2)}%)\n\n`
|
|
111
125
|
|
|
112
126
|
markdown += `## Diff\n\n`
|
|
113
127
|
|
|
@@ -117,7 +131,10 @@ function generateDiffMarkdown(prData, mainData, dependencies) {
|
|
|
117
131
|
|
|
118
132
|
const significantChanges = sortedDiffs.filter(
|
|
119
133
|
([, stats]) =>
|
|
120
|
-
|
|
134
|
+
(typeof stats.percentChange === "number" &&
|
|
135
|
+
(Math.abs(stats.percentChange) > 1 || Math.abs(stats.diff) > 1024)) ||
|
|
136
|
+
stats.percentChange === "Added" ||
|
|
137
|
+
stats.percentChange === "Removed",
|
|
121
138
|
)
|
|
122
139
|
|
|
123
140
|
if (significantChanges.length > 0) {
|
|
@@ -127,8 +144,7 @@ function generateDiffMarkdown(prData, mainData, dependencies) {
|
|
|
127
144
|
for (const [name, stats] of significantChanges) {
|
|
128
145
|
const version = dependencies[name]
|
|
129
146
|
const symbol = stats.diff > 0 ? "📈" : "📉"
|
|
130
|
-
markdown += `| ${name}@${version} | ${formatBytes(stats.before)} | ${formatBytes(stats.after)} | ${symbol} ${formatBytes(Math.abs(stats.diff))} |
|
|
131
|
-
${stats.percentChange.toFixed(2)}% |\n`
|
|
147
|
+
markdown += `| ${name}@${version} | ${formatBytes(stats.before)} | ${formatBytes(stats.after)} | ${symbol} ${formatBytes(Math.abs(stats.diff))} | ${typeof stats.percentChange === "number" ? stats.percentChange.toFixed(2) + "%" : stats.percentChange} |\n`
|
|
132
148
|
}
|
|
133
149
|
} else {
|
|
134
150
|
markdown += "No significant changes in bundle size.\n"
|
|
@@ -165,6 +165,14 @@ export default function AIChatInterface({
|
|
|
165
165
|
{messages.length === 0 && isLoggedIn && (
|
|
166
166
|
<div className="text-gray-500 text-xl text-center pt-[30vh] flex flex-col items-center">
|
|
167
167
|
<div>Submit a prompt to {snippet ? "edit!" : "get started!"}</div>
|
|
168
|
+
<div className="mt-2">
|
|
169
|
+
This is our legacy AI chat interface. For a better experience,
|
|
170
|
+
please use{" "}
|
|
171
|
+
<PrefetchPageLink href="https://chat.tscircuit.com">
|
|
172
|
+
chat.tscircuit.com
|
|
173
|
+
</PrefetchPageLink>
|
|
174
|
+
.
|
|
175
|
+
</div>
|
|
168
176
|
<div className="text-6xl mt-4">↓</div>
|
|
169
177
|
</div>
|
|
170
178
|
)}
|
|
@@ -3,7 +3,7 @@ import posthog from "posthog-js"
|
|
|
3
3
|
import CookieConsent from "react-cookie-consent"
|
|
4
4
|
|
|
5
5
|
posthog.init("phc_htd8AQjSfVEsFCLQMAiUooG4Q0DKBCjqYuQglc9V3Wo", {
|
|
6
|
-
api_host: "https://
|
|
6
|
+
api_host: "https://postpig.tscircuit.com",
|
|
7
7
|
person_profiles: "always",
|
|
8
8
|
})
|
|
9
9
|
|
|
@@ -166,6 +166,15 @@ export function CodeAndPreview({ snippet }: Props) {
|
|
|
166
166
|
const [lastSavedAt, setLastSavedAt] = useState(Date.now())
|
|
167
167
|
|
|
168
168
|
const handleSave = async () => {
|
|
169
|
+
if (hasUnrunChanges) {
|
|
170
|
+
toast({
|
|
171
|
+
title: "Warning",
|
|
172
|
+
description: "You must run the snippet before saving your changes.",
|
|
173
|
+
variant: "destructive",
|
|
174
|
+
})
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
169
178
|
setLastSavedAt(Date.now())
|
|
170
179
|
if (snippet) {
|
|
171
180
|
updateSnippetMutation.mutate()
|
|
@@ -197,8 +206,6 @@ export function CodeAndPreview({ snippet }: Props) {
|
|
|
197
206
|
...(code.match(/export const (\w+) ?=/)?.slice(1) ?? []),
|
|
198
207
|
]
|
|
199
208
|
|
|
200
|
-
console.log(possibleExportNames)
|
|
201
|
-
|
|
202
209
|
const exportName = possibleExportNames[0]
|
|
203
210
|
|
|
204
211
|
let entrypointContent: string
|
|
@@ -224,7 +231,6 @@ export function CodeAndPreview({ snippet }: Props) {
|
|
|
224
231
|
"main.tsx": entrypointContent,
|
|
225
232
|
}
|
|
226
233
|
}, [code, manualEditsFileContent])
|
|
227
|
-
console.log(fsMap)
|
|
228
234
|
|
|
229
235
|
if (!snippet && (urlParams.snippet_id || urlParams.should_create_snippet)) {
|
|
230
236
|
return (
|
|
@@ -12,12 +12,16 @@ export function CreateNewSnippetWithAiHero() {
|
|
|
12
12
|
const handleSubmit = (e: React.FormEvent) => {
|
|
13
13
|
e.preventDefault()
|
|
14
14
|
if (inputValue.trim()) {
|
|
15
|
-
navigate(
|
|
15
|
+
navigate(
|
|
16
|
+
`https://chat.tscircuit.com?initial_prompt=${encodeURIComponent(inputValue)}`,
|
|
17
|
+
)
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const handleQuickPrompt = (prompt: string) => {
|
|
20
|
-
navigate(
|
|
22
|
+
navigate(
|
|
23
|
+
`https://chat.tscircuit.com?initial_prompt=${encodeURIComponent(prompt)}`,
|
|
24
|
+
)
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
return (
|
|
@@ -48,6 +48,7 @@ import { SnippetLink } from "./SnippetLink"
|
|
|
48
48
|
import { TypeBadge } from "./TypeBadge"
|
|
49
49
|
import { useUpdateDescriptionDialog } from "./dialogs/edit-description-dialog"
|
|
50
50
|
import { useForkSnippetMutation } from "@/hooks/useForkSnippetMutation"
|
|
51
|
+
import tscircuitCorePkg from "@tscircuit/core/package.json"
|
|
51
52
|
|
|
52
53
|
export default function EditorNav({
|
|
53
54
|
circuitJson,
|
|
@@ -367,6 +368,9 @@ export default function EditorNav({
|
|
|
367
368
|
<Trash2 className="mr-2 h-3 w-3" />
|
|
368
369
|
Delete Snippet
|
|
369
370
|
</DropdownMenuItem>
|
|
371
|
+
<DropdownMenuItem className="text-xs text-gray-500" disabled>
|
|
372
|
+
@tscircuit/core@{tscircuitCorePkg.version}
|
|
373
|
+
</DropdownMenuItem>
|
|
370
374
|
</DropdownMenuContent>
|
|
371
375
|
</DropdownMenu>
|
|
372
376
|
<Button
|
|
@@ -26,7 +26,7 @@ export default function Footer() {
|
|
|
26
26
|
{ name: "Home", href: "/" },
|
|
27
27
|
{ name: "Dashboard", href: "/dashboard" },
|
|
28
28
|
{ name: "Editor", href: "/editor" },
|
|
29
|
-
{ name: "Create with AI", href: "
|
|
29
|
+
{ name: "Create with AI", href: "https://chat.tscircuit.com" },
|
|
30
30
|
{
|
|
31
31
|
name: "My Profile",
|
|
32
32
|
href: `/${session?.github_username}`,
|
|
@@ -75,16 +75,15 @@ export default function Header() {
|
|
|
75
75
|
<HeaderButton href="/dashboard">Dashboard</HeaderButton>
|
|
76
76
|
</li>
|
|
77
77
|
)}
|
|
78
|
-
<li>
|
|
79
|
-
<HeaderButton href="/newest">Newest</HeaderButton>
|
|
80
|
-
</li>
|
|
81
78
|
<li>
|
|
82
79
|
<HeaderButton href="/quickstart" alsoHighlightForUrl="/editor">
|
|
83
80
|
Editor
|
|
84
81
|
</HeaderButton>
|
|
85
82
|
</li>
|
|
86
83
|
<li>
|
|
87
|
-
<
|
|
84
|
+
<a href="https://chat.tscircuit.com">
|
|
85
|
+
<Button variant="ghost">AI</Button>
|
|
86
|
+
</a>
|
|
88
87
|
</li>
|
|
89
88
|
<li>
|
|
90
89
|
<a href="https://docs.tscircuit.com">
|
|
@@ -144,11 +143,6 @@ export default function Header() {
|
|
|
144
143
|
</HeaderButton>
|
|
145
144
|
</li>
|
|
146
145
|
)}
|
|
147
|
-
<li>
|
|
148
|
-
<HeaderButton className="w-full justify-start" href="/newest">
|
|
149
|
-
Newest
|
|
150
|
-
</HeaderButton>
|
|
151
|
-
</li>
|
|
152
146
|
<li>
|
|
153
147
|
<HeaderButton
|
|
154
148
|
className="w-full justify-start"
|
|
@@ -159,7 +153,10 @@ export default function Header() {
|
|
|
159
153
|
</HeaderButton>
|
|
160
154
|
</li>
|
|
161
155
|
<li>
|
|
162
|
-
<HeaderButton
|
|
156
|
+
<HeaderButton
|
|
157
|
+
className="w-full justify-start"
|
|
158
|
+
href="https://chat.tscircuit.com"
|
|
159
|
+
>
|
|
163
160
|
AI
|
|
164
161
|
</HeaderButton>
|
|
165
162
|
</li>
|
|
@@ -41,7 +41,7 @@ export const HeaderLogin: React.FC<HeaderLoginProps> = () => {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
return (
|
|
44
|
-
<div className="flex items-center space-x-2 justify-end">
|
|
44
|
+
<div className="flex items-center md:space-x-2 justify-end">
|
|
45
45
|
<Button onClick={() => signIn()} variant="ghost">
|
|
46
46
|
Log in
|
|
47
47
|
</Button>
|
|
@@ -320,7 +320,10 @@ export const PreviewContent = ({
|
|
|
320
320
|
>
|
|
321
321
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
|
322
322
|
{circuitJson ? (
|
|
323
|
-
<CadViewer
|
|
323
|
+
<CadViewer
|
|
324
|
+
circuitJson={circuitJson as any}
|
|
325
|
+
ref={threeJsObjectRef}
|
|
326
|
+
/>
|
|
324
327
|
) : (
|
|
325
328
|
<PreviewEmptyState triggerRunTsx={triggerRunTsx} />
|
|
326
329
|
)}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Button } from "@/components/ui/button"
|
|
2
|
+
import { ChevronDown, ChevronUp, Star } from "lucide-react"
|
|
3
|
+
import { Link } from "wouter"
|
|
4
|
+
import { Snippet } from "fake-snippets-api/lib/db/schema"
|
|
5
|
+
|
|
6
|
+
interface SnippetListProps {
|
|
7
|
+
title: string
|
|
8
|
+
snippets?: Snippet[]
|
|
9
|
+
showAll?: boolean
|
|
10
|
+
onToggleShowAll?: () => void
|
|
11
|
+
maxItems?: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const SnippetList = ({
|
|
15
|
+
title,
|
|
16
|
+
snippets = [],
|
|
17
|
+
showAll = false,
|
|
18
|
+
onToggleShowAll,
|
|
19
|
+
maxItems = 5,
|
|
20
|
+
}: SnippetListProps) => {
|
|
21
|
+
const displayedSnippets = showAll ? snippets : snippets.slice(0, maxItems)
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div>
|
|
25
|
+
<div className="flex items-center justify-between">
|
|
26
|
+
<h2 className="text-sm font-bold text-gray-700">{title}</h2>
|
|
27
|
+
{snippets.length > maxItems && onToggleShowAll && (
|
|
28
|
+
<Button
|
|
29
|
+
variant="ghost"
|
|
30
|
+
size="sm"
|
|
31
|
+
onClick={onToggleShowAll}
|
|
32
|
+
className="text-blue-600 hover:text-blue-700 hover:bg-transparent"
|
|
33
|
+
>
|
|
34
|
+
{showAll ? (
|
|
35
|
+
<>
|
|
36
|
+
Show less <ChevronUp className="w-3 h-3 ml-1" />
|
|
37
|
+
</>
|
|
38
|
+
) : (
|
|
39
|
+
<>
|
|
40
|
+
Show more <ChevronDown className="w-3 h-3 ml-1" />
|
|
41
|
+
</>
|
|
42
|
+
)}
|
|
43
|
+
</Button>
|
|
44
|
+
)}
|
|
45
|
+
</div>
|
|
46
|
+
<div className="border-b border-gray-200" />
|
|
47
|
+
{snippets && (
|
|
48
|
+
<ul className="space-y-1 mt-2">
|
|
49
|
+
{displayedSnippets.map((snippet) => (
|
|
50
|
+
<li key={snippet.snippet_id}>
|
|
51
|
+
<div className="flex items-center">
|
|
52
|
+
<Link
|
|
53
|
+
href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
|
|
54
|
+
className="text-blue-600 hover:underline text-sm"
|
|
55
|
+
>
|
|
56
|
+
{snippet.owner_name}/{snippet.unscoped_name}
|
|
57
|
+
</Link>
|
|
58
|
+
{snippet.star_count > 0 && (
|
|
59
|
+
<span className="ml-2 text-gray-500 text-xs flex items-center">
|
|
60
|
+
<Star className="w-3 h-3 mr-1" />
|
|
61
|
+
{snippet.star_count}
|
|
62
|
+
</span>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
</li>
|
|
66
|
+
))}
|
|
67
|
+
</ul>
|
|
68
|
+
)}
|
|
69
|
+
</div>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
@@ -3,11 +3,12 @@ export const blinkingLedBoardTemplate = {
|
|
|
3
3
|
code: `
|
|
4
4
|
import { useUsbC } from "@tsci/seveibar.smd-usb-c"
|
|
5
5
|
import { useRedLed } from "@tsci/seveibar.red-led"
|
|
6
|
-
import {
|
|
6
|
+
import { useNE555P } from "@tsci/seveibar.a555timer"
|
|
7
7
|
|
|
8
8
|
export const MyBlinkingLedCircuit = () => {
|
|
9
9
|
const USBC = useUsbC("USBC")
|
|
10
10
|
const Led = useRedLed("LED")
|
|
11
|
+
const A555Timer = useNE555P("B1")
|
|
11
12
|
|
|
12
13
|
return (
|
|
13
14
|
<board width="30mm" height="30mm" schAutoLayoutEnabled>
|
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"
|