@tscircuit/cli 0.0.2

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 (110) hide show
  1. package/.prettierrc +1 -0
  2. package/README.md +50 -0
  3. package/build-cli.ts +14 -0
  4. package/bun.lockb +0 -0
  5. package/cli.ts +12 -0
  6. package/dev-server-api/README.md +3 -0
  7. package/dev-server-api/bun.lockb +0 -0
  8. package/dev-server-api/edgespec.config.ts +4 -0
  9. package/dev-server-api/index.ts +1 -0
  10. package/dev-server-api/package-lock.json +1201 -0
  11. package/dev-server-api/package.json +24 -0
  12. package/dev-server-api/routes/api/dev_package_examples/create.ts +43 -0
  13. package/dev-server-api/routes/api/dev_package_examples/get.ts +37 -0
  14. package/dev-server-api/routes/api/dev_package_examples/list.ts +29 -0
  15. package/dev-server-api/routes/api/dev_package_examples/update.ts +31 -0
  16. package/dev-server-api/routes/api/health.ts +11 -0
  17. package/dev-server-api/src/db/create-schema.ts +14 -0
  18. package/dev-server-api/src/db/get-db.ts +78 -0
  19. package/dev-server-api/src/middlewares/with-db.ts +14 -0
  20. package/dev-server-api/src/middlewares/with-error-response.ts +24 -0
  21. package/dev-server-api/src/with-edge-spec.ts +8 -0
  22. package/dev-server-api/tsconfig.json +28 -0
  23. package/dev-server-frontend/.eslintrc.cjs +20 -0
  24. package/dev-server-frontend/README.md +3 -0
  25. package/dev-server-frontend/bun.lockb +0 -0
  26. package/dev-server-frontend/components.json +17 -0
  27. package/dev-server-frontend/index.html +13 -0
  28. package/dev-server-frontend/package.json +62 -0
  29. package/dev-server-frontend/postcss.config.js +6 -0
  30. package/dev-server-frontend/src/App.tsx +22 -0
  31. package/dev-server-frontend/src/ExampleContentView.tsx +60 -0
  32. package/dev-server-frontend/src/Header.tsx +53 -0
  33. package/dev-server-frontend/src/HeaderMenu.tsx +188 -0
  34. package/dev-server-frontend/src/components/command-k.tsx +62 -0
  35. package/dev-server-frontend/src/components/global-context-providers.tsx +11 -0
  36. package/dev-server-frontend/src/components/select-example-search.tsx +137 -0
  37. package/dev-server-frontend/src/components/ui/alert-dialog.tsx +139 -0
  38. package/dev-server-frontend/src/components/ui/alert.tsx +59 -0
  39. package/dev-server-frontend/src/components/ui/breadcrumb.tsx +115 -0
  40. package/dev-server-frontend/src/components/ui/button.tsx +57 -0
  41. package/dev-server-frontend/src/components/ui/card.tsx +76 -0
  42. package/dev-server-frontend/src/components/ui/command.tsx +153 -0
  43. package/dev-server-frontend/src/components/ui/context-menu.tsx +202 -0
  44. package/dev-server-frontend/src/components/ui/dialog.tsx +120 -0
  45. package/dev-server-frontend/src/components/ui/menubar.tsx +238 -0
  46. package/dev-server-frontend/src/components/ui/navigation-menu.tsx +128 -0
  47. package/dev-server-frontend/src/components/ui/popover.tsx +31 -0
  48. package/dev-server-frontend/src/components/ui/select.tsx +162 -0
  49. package/dev-server-frontend/src/components/ui/tabs.tsx +53 -0
  50. package/dev-server-frontend/src/components/ui/toggle-group.tsx +59 -0
  51. package/dev-server-frontend/src/components/ui/toggle.tsx +43 -0
  52. package/dev-server-frontend/src/components/ui/tooltip.tsx +28 -0
  53. package/dev-server-frontend/src/hooks/toast-if-api-not-connected.ts +23 -0
  54. package/dev-server-frontend/src/hooks/use-global-store.ts +42 -0
  55. package/dev-server-frontend/src/index.css +76 -0
  56. package/dev-server-frontend/src/lib/utils.ts +6 -0
  57. package/dev-server-frontend/src/main.tsx +13 -0
  58. package/dev-server-frontend/src/vite-env.d.ts +1 -0
  59. package/dev-server-frontend/tailwind.config.js +77 -0
  60. package/dev-server-frontend/tsconfig.json +26 -0
  61. package/dev-server-frontend/tsconfig.node.json +13 -0
  62. package/dev-server-frontend/vite.config.ts +23 -0
  63. package/dist/cli.js +1326 -0
  64. package/lib/cmd-fns/auth-login.ts +59 -0
  65. package/lib/cmd-fns/auth-logout.ts +3 -0
  66. package/lib/cmd-fns/auth-sessions-create.ts +3 -0
  67. package/lib/cmd-fns/auth-sessions-get.ts +3 -0
  68. package/lib/cmd-fns/auth-sessions-list.ts +5 -0
  69. package/lib/cmd-fns/config-print-config.ts +6 -0
  70. package/lib/cmd-fns/config-reveal-location.ts +5 -0
  71. package/lib/cmd-fns/config-set-log-requests.ts +7 -0
  72. package/lib/cmd-fns/config-set-registry.ts +7 -0
  73. package/lib/cmd-fns/config-set-session.ts +7 -0
  74. package/lib/cmd-fns/dev.ts +217 -0
  75. package/lib/cmd-fns/index.ts +27 -0
  76. package/lib/cmd-fns/package-examples-create.ts +33 -0
  77. package/lib/cmd-fns/package-examples-get.ts +20 -0
  78. package/lib/cmd-fns/package-examples-list.ts +18 -0
  79. package/lib/cmd-fns/package-files-create.ts +31 -0
  80. package/lib/cmd-fns/package-files-download.ts +29 -0
  81. package/lib/cmd-fns/package-files-get.ts +3 -0
  82. package/lib/cmd-fns/package-files-list.ts +28 -0
  83. package/lib/cmd-fns/package-files-upload-directory.ts +6 -0
  84. package/lib/cmd-fns/package-releases-create.ts +35 -0
  85. package/lib/cmd-fns/package-releases-get.ts +3 -0
  86. package/lib/cmd-fns/package-releases-list.ts +32 -0
  87. package/lib/cmd-fns/packages-create.ts +16 -0
  88. package/lib/cmd-fns/packages-get.ts +16 -0
  89. package/lib/cmd-fns/packages-list.ts +16 -0
  90. package/lib/cmd-fns/publish.ts +3 -0
  91. package/lib/cmd-fns/soupify.ts +27 -0
  92. package/lib/get-program.ts +191 -0
  93. package/lib/param-handlers/index.ts +16 -0
  94. package/lib/param-handlers/interact-for-local-directory.ts +58 -0
  95. package/lib/param-handlers/interact-for-local-file.ts +59 -0
  96. package/lib/param-handlers/interact-for-package-example-id.ts +25 -0
  97. package/lib/param-handlers/interact-for-package-name-with-version.ts +63 -0
  98. package/lib/param-handlers/interact-for-package-name.ts +45 -0
  99. package/lib/param-handlers/interact-for-package-release-id.ts +16 -0
  100. package/lib/param-handlers/param-handler-type.ts +7 -0
  101. package/lib/soupify.ts +71 -0
  102. package/lib/util/app-context.ts +14 -0
  103. package/lib/util/create-context-and-run-program.ts +116 -0
  104. package/package.json +62 -0
  105. package/tests/assets/example-project/examples/basic-capacitor.tsx +1 -0
  106. package/tests/assets/example-project/examples/basic-resistor.tsx +3 -0
  107. package/tests/assets/example-project/package.json +8 -0
  108. package/tests/assets/example-project/src/MyCircuit.tsx +3 -0
  109. package/tests/soupify.test.ts +9 -0
  110. package/tsconfig.json +109 -0
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@tscircuit/dev-server-api",
3
+ "main": "dist/bundle.ts",
4
+ "type": "module",
5
+ "scripts": {
6
+ "start": "edgespec dev --port 3021",
7
+ "build": "edgespec bundle -o dist/bundle.ts"
8
+ },
9
+ "devDependencies": {
10
+ "@types/better-sqlite3": "^7.6.9",
11
+ "@types/bun": "latest",
12
+ "@types/sql.js": "^1.4.9"
13
+ },
14
+ "peerDependencies": {
15
+ "typescript": "^5.0.0"
16
+ },
17
+ "dependencies": {
18
+ "better-sqlite3": "^9.4.3",
19
+ "edgespec": "^0.0.69",
20
+ "kysely": "^0.27.3",
21
+ "kysely-bun-sqlite": "^0.3.2",
22
+ "zod": "^3.22.4"
23
+ }
24
+ }
@@ -0,0 +1,43 @@
1
+ import { withEdgeSpec } from "src/with-edge-spec"
2
+ import { NotFoundError } from "edgespec/middleware"
3
+ import { z } from "zod"
4
+
5
+ export default withEdgeSpec({
6
+ methods: ["POST"],
7
+ jsonBody: z.object({
8
+ file_path: z.string(),
9
+ export_name: z.string().default("default"),
10
+ tscircuit_soup: z.any(),
11
+ }),
12
+ jsonResponse: z.object({
13
+ dev_package_example: z.object({
14
+ dev_package_example_id: z.coerce.number(),
15
+ file_path: z.string(),
16
+ tscircuit_soup: z.any(),
17
+ last_updated_at: z.string().datetime(),
18
+ }),
19
+ }),
20
+ auth: "none",
21
+ })(async (req, ctx) => {
22
+ const dev_package_example = await ctx.db
23
+ .insertInto("dev_package_example")
24
+ .values({
25
+ file_path: req.jsonBody.file_path,
26
+ export_name: req.jsonBody.export_name,
27
+ tscircuit_soup: JSON.stringify(req.jsonBody.tscircuit_soup),
28
+ last_updated_at: new Date().toISOString(),
29
+ })
30
+ .onConflict((oc) =>
31
+ oc.columns(["file_path"]).doUpdateSet({
32
+ export_name: req.jsonBody.export_name,
33
+ tscircuit_soup: JSON.stringify(req.jsonBody.tscircuit_soup),
34
+ last_updated_at: new Date().toISOString(),
35
+ })
36
+ )
37
+ .returningAll()
38
+ .executeTakeFirstOrThrow()
39
+
40
+ return ctx.json({
41
+ dev_package_example,
42
+ })
43
+ })
@@ -0,0 +1,37 @@
1
+ import { withEdgeSpec } from "src/with-edge-spec"
2
+ import { NotFoundError } from "edgespec/middleware"
3
+ import { z } from "zod"
4
+
5
+ export default withEdgeSpec({
6
+ methods: ["GET", "POST"],
7
+ commonParams: z.object({
8
+ dev_package_example_id: z.coerce.number(),
9
+ }),
10
+ jsonResponse: z.object({
11
+ dev_package_example: z.object({
12
+ dev_package_example_id: z.coerce.number(),
13
+ file_path: z.string(),
14
+ tscircuit_soup: z.any(),
15
+ last_updated_at: z.string().datetime(),
16
+ }),
17
+ }),
18
+ auth: "none",
19
+ })(async (req, ctx) => {
20
+ return ctx.json({
21
+ dev_package_example: await ctx.db
22
+ .selectFrom("dev_package_example")
23
+ .selectAll()
24
+ .where(
25
+ "dev_package_example_id",
26
+ "=",
27
+ req.commonParams.dev_package_example_id
28
+ )
29
+ .executeTakeFirstOrThrow((e) => {
30
+ throw new NotFoundError("Package not found")
31
+ })
32
+ .then((r) => ({
33
+ ...r,
34
+ tscircuit_soup: JSON.parse(r.tscircuit_soup),
35
+ })),
36
+ })
37
+ })
@@ -0,0 +1,29 @@
1
+ import { withEdgeSpec } from "src/with-edge-spec"
2
+ import { z } from "zod"
3
+
4
+ export default withEdgeSpec({
5
+ methods: ["GET", "POST"],
6
+ jsonResponse: z.object({
7
+ dev_package_examples: z.array(
8
+ z.object({
9
+ dev_package_example_id: z.coerce.number(),
10
+ file_path: z.string(),
11
+ export_name: z.string(),
12
+ last_updated_at: z.string().datetime(),
13
+ })
14
+ ),
15
+ }),
16
+ auth: "none",
17
+ })(async (req, ctx) => {
18
+ return ctx.json({
19
+ dev_package_examples: await ctx.db
20
+ .selectFrom("dev_package_example")
21
+ .select([
22
+ "dev_package_example_id",
23
+ "file_path",
24
+ "export_name",
25
+ "last_updated_at",
26
+ ])
27
+ .execute(),
28
+ })
29
+ })
@@ -0,0 +1,31 @@
1
+ import { withEdgeSpec } from "src/with-edge-spec"
2
+ import { NotFoundError } from "edgespec/middleware"
3
+ import { z } from "zod"
4
+
5
+ export default withEdgeSpec({
6
+ methods: ["POST"],
7
+ jsonBody: z.object({
8
+ dev_package_example_id: z.coerce.number(),
9
+ tscircuit_soup: z.any(),
10
+ }),
11
+ jsonResponse: z.object({
12
+ dev_package_example: z.object({
13
+ dev_package_example_id: z.coerce.number(),
14
+ tscircuit_soup: z.any(),
15
+ last_updated_at: z.string().datetime(),
16
+ }),
17
+ }),
18
+ auth: "none",
19
+ })(async (req, ctx) => {
20
+ return ctx.json({
21
+ dev_package_example: await ctx.db
22
+ .updateTable("dev_package_example")
23
+ .set({
24
+ tscircuit_soup: req.jsonBody.tscircuit_soup,
25
+ last_updated_at: new Date().toISOString(),
26
+ })
27
+ .returningAll()
28
+ .where("dev_package_example_id", "=", req.jsonBody.dev_package_example_id)
29
+ .executeTakeFirstOrThrow(),
30
+ })
31
+ })
@@ -0,0 +1,11 @@
1
+ import { withEdgeSpec } from "../../src/with-edge-spec"
2
+ import { z } from "zod"
3
+
4
+ export default withEdgeSpec({
5
+ methods: ["GET", "POST"],
6
+ jsonResponse: z.object({
7
+ ok: z.boolean(),
8
+ }),
9
+ })(async (req, ctx) => {
10
+ return ctx.json({ ok: true })
11
+ })
@@ -0,0 +1,14 @@
1
+ import type { DbClient } from "./get-db"
2
+
3
+ export const createSchema = async (db: DbClient) => {
4
+ await db.schema
5
+ .createTable("dev_package_example")
6
+ .addColumn("dev_package_example_id", "integer", (col) =>
7
+ col.primaryKey().autoIncrement()
8
+ )
9
+ .addColumn("file_path", "text", (col) => col.unique())
10
+ .addColumn("export_name", "text")
11
+ .addColumn("tscircuit_soup", "json")
12
+ .addColumn("last_updated_at", "text")
13
+ .execute()
14
+ }
@@ -0,0 +1,78 @@
1
+ import { Kysely, sql, type Generated, SqliteDialect } from "kysely"
2
+ import { createSchema } from "./create-schema"
3
+ import { mkdirSync } from "fs"
4
+ import * as Path from "path"
5
+
6
+ interface DevPackageExample {
7
+ dev_package_example_id: Generated<number>
8
+ tscircuit_soup: any
9
+ file_path: string
10
+ export_name: string
11
+ last_updated_at: string
12
+ }
13
+
14
+ interface KyselyDatabaseSchema {
15
+ dev_package_example: DevPackageExample
16
+ }
17
+
18
+ export type DbClient = Kysely<KyselyDatabaseSchema>
19
+
20
+ // let globalDb: Database | undefined
21
+
22
+ let globalDb: Kysely<KyselyDatabaseSchema> | undefined
23
+
24
+ export const getDb = async (): Promise<Kysely<KyselyDatabaseSchema>> => {
25
+ if (globalDb) return globalDb
26
+
27
+ const devServerDbPath =
28
+ process.env.TSCI_DEV_SERVER_DB ?? "./.tscircuit/dev-server.db"
29
+
30
+ mkdirSync(Path.dirname(devServerDbPath), { recursive: true })
31
+
32
+ // better-sqlite3 doesn't work in bun, so if we see we can use the bun
33
+ // alternative, attempt to use that instead
34
+ let dialect: any
35
+
36
+ if (typeof Bun !== "undefined") {
37
+ // console.log("Attempting to use bun-sqlite")
38
+ try {
39
+ const { BunSqliteDialect } = await import("kysely-bun-sqlite")
40
+ const { Database } = await import("bun:sqlite")
41
+ dialect = new BunSqliteDialect({
42
+ database: new Database(devServerDbPath, {
43
+ create: true,
44
+ }),
45
+ })
46
+ } catch (e) {}
47
+ }
48
+
49
+ if (!dialect) {
50
+ // console.log("Attempting to use better-sqlite3")
51
+ try {
52
+ const BetterSqlite3 = await import("better-sqlite3")
53
+ dialect = new SqliteDialect({
54
+ database: new BetterSqlite3.default(devServerDbPath),
55
+ })
56
+ } catch (e) {}
57
+ }
58
+
59
+ if (!dialect) {
60
+ throw new Error("Was not able to load sqlite dialect")
61
+ }
62
+
63
+ const db = new Kysely<KyselyDatabaseSchema>({
64
+ dialect,
65
+ })
66
+
67
+ const schemaExistsResult = await sql`
68
+ SELECT name
69
+ FROM sqlite_master
70
+ WHERE type='table' AND name='dev_package_example'
71
+ `.execute(db)
72
+
73
+ if (schemaExistsResult.rows.length === 0) {
74
+ await createSchema(db)
75
+ }
76
+
77
+ return db
78
+ }
@@ -0,0 +1,14 @@
1
+ import type { Middleware } from "edgespec"
2
+ import { getDb, type DbClient } from "../db/get-db"
3
+
4
+ export const withDb: Middleware<
5
+ {},
6
+ {
7
+ db: DbClient
8
+ }
9
+ > = async (req, ctx, next) => {
10
+ if (!ctx.db) {
11
+ ctx.db = await getDb()
12
+ }
13
+ return next(req, ctx)
14
+ }
@@ -0,0 +1,24 @@
1
+ import type { Middleware } from "edgespec/middleware"
2
+ import kleur from "kleur"
3
+
4
+ export const withErrorResponse: Middleware<{}, {}> = async (req, ctx, next) => {
5
+ try {
6
+ return await next(req, ctx)
7
+ } catch (error: any) {
8
+ console.error(kleur.red("Intercepted error:"), error)
9
+ // If error is a Response, return it
10
+ if (error instanceof Response) {
11
+ return error
12
+ }
13
+ return (ctx as any).json(
14
+ {
15
+ ok: false,
16
+ error: {
17
+ message: error?.message,
18
+ error_code: error?.error_code ?? "internal_server_error",
19
+ },
20
+ },
21
+ { status: error?.status || 500 }
22
+ )
23
+ }
24
+ }
@@ -0,0 +1,8 @@
1
+ import { createWithEdgeSpec } from "edgespec"
2
+ import { withDb } from "./middlewares/with-db"
3
+ import { withErrorResponse } from "./middlewares/with-error-response"
4
+
5
+ export const withEdgeSpec = createWithEdgeSpec({
6
+ authMiddleware: {},
7
+ beforeAuthMiddleware: [withErrorResponse, withDb],
8
+ })
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Enable latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "ESNext",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+ "baseUrl": ".",
11
+
12
+ // Bundler mode
13
+ "moduleResolution": "bundler",
14
+ "allowImportingTsExtensions": true,
15
+ "verbatimModuleSyntax": true,
16
+ "noEmit": true,
17
+
18
+ // Best practices
19
+ "strict": true,
20
+ "skipLibCheck": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+
23
+ // Some stricter flags (disabled by default)
24
+ "noUnusedLocals": false,
25
+ "noUnusedParameters": false,
26
+ "noPropertyAccessFromIndexSignature": false
27
+ }
28
+ }
@@ -0,0 +1,20 @@
1
+ module.exports = {
2
+ root: true,
3
+ env: { browser: true, es2020: true },
4
+ extends: [
5
+ 'eslint:recommended',
6
+ 'plugin:@typescript-eslint/recommended',
7
+ 'plugin:react-hooks/recommended',
8
+ ],
9
+ ignorePatterns: ['dist', '.eslintrc.cjs'],
10
+ parser: '@typescript-eslint/parser',
11
+ plugins: ['react-refresh'],
12
+ rules: {
13
+ 'react-refresh/only-export-components': [
14
+ 'warn',
15
+ { allowConstantExport: true },
16
+ ],
17
+ '@typescript-eslint/no-unused-vars': 'off',
18
+ "@typescript-eslint/no-explicit-any": "off"
19
+ },
20
+ }
@@ -0,0 +1,3 @@
1
+ # @tscircuit/dev-server-frontend
2
+
3
+ Frontend for the tscircuit dev server.
Binary file
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "tailwind.config.js",
8
+ "css": "src/index.css",
9
+ "baseColor": "gray",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "src/components",
15
+ "utils": "src/lib/utils"
16
+ }
17
+ }
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>[dev] tscircuit</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@tscircuit/dev-server-frontend",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "main": "dist/bundle.ts",
7
+ "exports": {
8
+ ".": "./dist/bundle.ts"
9
+ },
10
+ "scripts": {
11
+ "dev": "vite",
12
+ "start": "npm run dev",
13
+ "build": "tsc && vite build --base /preview && rm -f dist/bundle* && make-vfs --dir dist --outfile ./dist/bundle.ts",
14
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 10",
15
+ "preview": "vite preview"
16
+ },
17
+ "dependencies": {
18
+ "@radix-ui/react-alert-dialog": "^1.0.5",
19
+ "@radix-ui/react-context-menu": "^2.1.5",
20
+ "@radix-ui/react-dialog": "^1.0.5",
21
+ "@radix-ui/react-icons": "^1.3.0",
22
+ "@radix-ui/react-menubar": "^1.0.4",
23
+ "@radix-ui/react-navigation-menu": "^1.1.4",
24
+ "@radix-ui/react-popover": "^1.0.7",
25
+ "@radix-ui/react-select": "^2.0.0",
26
+ "@radix-ui/react-slot": "^1.0.2",
27
+ "@radix-ui/react-tabs": "^1.0.4",
28
+ "@radix-ui/react-toggle": "^1.0.3",
29
+ "@radix-ui/react-toggle-group": "^1.0.4",
30
+ "@radix-ui/react-tooltip": "^1.0.7",
31
+ "@tscircuit/pcb-viewer": "^1.2.12",
32
+ "@tscircuit/schematic-viewer": "^1.1.5",
33
+ "axios": "^1.6.7",
34
+ "class-variance-authority": "^0.7.0",
35
+ "clsx": "^2.1.0",
36
+ "cmdk": "^1.0.0",
37
+ "react": "^18.2.0",
38
+ "react-dom": "^18.2.0",
39
+ "react-hot-toast": "^2.4.1",
40
+ "react-query": "^3.39.3",
41
+ "tailwind-merge": "^2.2.1",
42
+ "tailwindcss-animate": "^1.0.7",
43
+ "zod": "^3.22.4",
44
+ "zustand": "^4.5.2"
45
+ },
46
+ "devDependencies": {
47
+ "@types/react": "^18.2.64",
48
+ "@types/react-dom": "^18.2.21",
49
+ "@typescript-eslint/eslint-plugin": "^7.1.1",
50
+ "@typescript-eslint/parser": "^7.1.1",
51
+ "@vitejs/plugin-react": "^4.2.1",
52
+ "autoprefixer": "^10.4.18",
53
+ "eslint": "^8.57.0",
54
+ "eslint-plugin-react-hooks": "^4.6.0",
55
+ "eslint-plugin-react-refresh": "^0.4.5",
56
+ "make-vfs": "1.0.10",
57
+ "postcss": "^8.4.35",
58
+ "tailwindcss": "^3.4.1",
59
+ "typescript": "^5.2.2",
60
+ "vite": "^5.1.6"
61
+ }
62
+ }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -0,0 +1,22 @@
1
+ import { TabsContent } from "./components/ui/tabs"
2
+ import { MenubarShortcut } from "src/components/ui/menubar"
3
+ import { CommandK } from "./components/command-k"
4
+ import { useToastIfApiNotConnected } from "./hooks/toast-if-api-not-connected"
5
+ import { Header } from "./Header"
6
+ import { ExampleContentView } from "./ExampleContentView"
7
+
8
+ function App() {
9
+ useToastIfApiNotConnected()
10
+
11
+ return (
12
+ <div className="">
13
+ <Header />
14
+ <div className="">
15
+ <ExampleContentView />
16
+ </div>
17
+ <CommandK />
18
+ </div>
19
+ )
20
+ }
21
+
22
+ export default App
@@ -0,0 +1,60 @@
1
+ import { useQuery } from "react-query"
2
+ import { useGlobalStore } from "./hooks/use-global-store"
3
+ import axios from "axios"
4
+ import { Schematic } from "@tscircuit/schematic-viewer"
5
+ import { PCBViewer } from "@tscircuit/pcb-viewer"
6
+ import { cn } from "./lib/utils"
7
+
8
+ export const ExampleContentView = () => {
9
+ const devExamplePackageId = useGlobalStore(
10
+ (s) => s.active_dev_example_package_id
11
+ )
12
+
13
+ const { data: pkg } = useQuery(
14
+ ["dev_package_example", devExamplePackageId],
15
+ async () =>
16
+ axios
17
+ .post(`/api/dev_package_examples/get`, {
18
+ dev_package_example_id: devExamplePackageId,
19
+ })
20
+ .then((r) => r.data.dev_package_example)
21
+ )
22
+
23
+ const viewMode = useGlobalStore((s) => s.view_mode)
24
+ const splitMode = useGlobalStore((s) => s.split_mode)
25
+
26
+ const editorHeight = window.innerHeight - 52
27
+ const halfHeight = Math.floor(editorHeight / 2)
28
+
29
+ const itemHeight =
30
+ viewMode === "split" && splitMode === "vertical" ? halfHeight : editorHeight
31
+
32
+ return (
33
+ <div
34
+ key={pkg?.last_updated_at}
35
+ className={cn(
36
+ `h-[${editorHeight}px]`,
37
+ viewMode === "split" &&
38
+ splitMode === "horizontal" &&
39
+ "grid grid-cols-2",
40
+ viewMode === "split" && splitMode === "vertical" && "grid grid-rows-2"
41
+ )}
42
+ >
43
+ {pkg && (viewMode === "schematic" || viewMode === "split") && (
44
+ <Schematic
45
+ key={pkg?.last_updated_at}
46
+ style={{ height: itemHeight }}
47
+ soup={pkg.tscircuit_soup}
48
+ showTable={false}
49
+ />
50
+ )}
51
+ {pkg && (viewMode === "pcb" || viewMode === "split") && (
52
+ <PCBViewer
53
+ key={pkg?.last_updated_at}
54
+ height={itemHeight}
55
+ soup={pkg.tscircuit_soup}
56
+ />
57
+ )}
58
+ </div>
59
+ )
60
+ }
@@ -0,0 +1,53 @@
1
+ import { useState } from "react"
2
+ import { SelectExampleSearch } from "./components/select-example-search"
3
+ import { Button } from "./components/ui/button"
4
+ import { Tabs, TabsList, TabsTrigger } from "./components/ui/tabs"
5
+ import { RotateCounterClockwiseIcon } from "@radix-ui/react-icons"
6
+ import { useGlobalStore } from "./hooks/use-global-store"
7
+ import { HeaderMenu } from "./HeaderMenu"
8
+ import { CommandShortcut } from "./components/ui/command"
9
+
10
+ export const Header = () => {
11
+ const [viewMode, setViewMode] = useGlobalStore((s) => [
12
+ s.view_mode,
13
+ s.setViewMode,
14
+ ])
15
+ const [splitMode, setSplitMode] = useGlobalStore((s) => [
16
+ s.split_mode,
17
+ s.setSplitMode,
18
+ ])
19
+ const [inDebugMode, setInDebugMode] = useState(false)
20
+
21
+ return (
22
+ <div className="p-2 border-b border-b-gray-200 shadow-sm grid grid-cols-3">
23
+ <div className="flex">
24
+ <div className="inline-flex">
25
+ <HeaderMenu />
26
+ </div>
27
+ </div>
28
+ <div className="justify-center flex">
29
+ <SelectExampleSearch />
30
+ </div>
31
+ <div className="flex justify-end">
32
+ {/* Tabs from shadcn with "Tab One" and "Tab Two" */}
33
+ <Tabs onValueChange={(v) => setViewMode(v as any)} value={viewMode}>
34
+ <TabsList>
35
+ <TabsTrigger value="schematic">Schematic</TabsTrigger>
36
+ <TabsTrigger value="pcb">PCB</TabsTrigger>
37
+ <TabsTrigger value="split">Split</TabsTrigger>
38
+ </TabsList>
39
+ </Tabs>
40
+ <Button
41
+ variant="outline"
42
+ disabled={viewMode !== "split"}
43
+ className="ml-1 group"
44
+ onClick={() => {
45
+ setSplitMode(splitMode === "horizontal" ? "vertical" : "horizontal")
46
+ }}
47
+ >
48
+ <RotateCounterClockwiseIcon className="scale-x-[-1] group-hover:rotate-[30deg] transition-transform" />
49
+ </Button>
50
+ </div>
51
+ </div>
52
+ )
53
+ }