@tscircuit/fake-snippets 0.0.79 → 0.0.81

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
@@ -435,6 +435,8 @@ declare const packageReleaseSchema: z.ZodObject<{
435
435
  circuit_json_build_completed_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
436
436
  circuit_json_build_logs: z.ZodDefault<z.ZodArray<z.ZodAny, "many">>;
437
437
  circuit_json_build_is_stale: z.ZodDefault<z.ZodBoolean>;
438
+ ai_review_text: z.ZodDefault<z.ZodNullable<z.ZodString>>;
439
+ ai_review_requested: z.ZodDefault<z.ZodBoolean>;
438
440
  }, "strip", z.ZodTypeAny, {
439
441
  package_release_id: string;
440
442
  created_at: string;
@@ -452,6 +454,8 @@ declare const packageReleaseSchema: z.ZodObject<{
452
454
  circuit_json_build_in_progress: boolean;
453
455
  circuit_json_build_logs: any[];
454
456
  circuit_json_build_is_stale: boolean;
457
+ ai_review_text: string | null;
458
+ ai_review_requested: boolean;
455
459
  commit_sha?: string | null | undefined;
456
460
  license?: string | null | undefined;
457
461
  circuit_json_build_error?: string | null | undefined;
@@ -491,6 +495,8 @@ declare const packageReleaseSchema: z.ZodObject<{
491
495
  circuit_json_build_completed_at?: string | null | undefined;
492
496
  circuit_json_build_logs?: any[] | undefined;
493
497
  circuit_json_build_is_stale?: boolean | undefined;
498
+ ai_review_text?: string | null | undefined;
499
+ ai_review_requested?: boolean | undefined;
494
500
  }>;
495
501
  type PackageRelease = z.infer<typeof packageReleaseSchema>;
496
502
  declare const packageFileSchema: z.ZodObject<{
@@ -760,6 +766,8 @@ declare const createDatabase: ({ seed }?: {
760
766
  circuit_json_build_in_progress: boolean;
761
767
  circuit_json_build_logs: any[];
762
768
  circuit_json_build_is_stale: boolean;
769
+ ai_review_text: string | null;
770
+ ai_review_requested: boolean;
763
771
  commit_sha?: string | null | undefined;
764
772
  license?: string | null | undefined;
765
773
  circuit_json_build_error?: string | null | undefined;
@@ -1070,6 +1078,8 @@ declare const createDatabase: ({ seed }?: {
1070
1078
  circuit_json_build_in_progress: boolean;
1071
1079
  circuit_json_build_logs: any[];
1072
1080
  circuit_json_build_is_stale: boolean;
1081
+ ai_review_text: string | null;
1082
+ ai_review_requested: boolean;
1073
1083
  commit_sha?: string | null | undefined;
1074
1084
  license?: string | null | undefined;
1075
1085
  circuit_json_build_error?: string | null | undefined;
package/dist/index.js CHANGED
@@ -165,7 +165,10 @@ var packageReleaseSchema = z.object({
165
165
  circuit_json_build_started_at: z.string().datetime().nullable().optional(),
166
166
  circuit_json_build_completed_at: z.string().datetime().nullable().optional(),
167
167
  circuit_json_build_logs: z.array(z.any()).default([]),
168
- circuit_json_build_is_stale: z.boolean().default(false)
168
+ circuit_json_build_is_stale: z.boolean().default(false),
169
+ // AI Review
170
+ ai_review_text: z.string().nullable().default(null),
171
+ ai_review_requested: z.boolean().default(false)
169
172
  });
170
173
  var packageFileSchema = z.object({
171
174
  package_file_id: z.string(),
package/dist/schema.d.ts CHANGED
@@ -560,6 +560,8 @@ declare const packageReleaseSchema: z.ZodObject<{
560
560
  circuit_json_build_completed_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
561
561
  circuit_json_build_logs: z.ZodDefault<z.ZodArray<z.ZodAny, "many">>;
562
562
  circuit_json_build_is_stale: z.ZodDefault<z.ZodBoolean>;
563
+ ai_review_text: z.ZodDefault<z.ZodNullable<z.ZodString>>;
564
+ ai_review_requested: z.ZodDefault<z.ZodBoolean>;
563
565
  }, "strip", z.ZodTypeAny, {
564
566
  package_release_id: string;
565
567
  created_at: string;
@@ -577,6 +579,8 @@ declare const packageReleaseSchema: z.ZodObject<{
577
579
  circuit_json_build_in_progress: boolean;
578
580
  circuit_json_build_logs: any[];
579
581
  circuit_json_build_is_stale: boolean;
582
+ ai_review_text: string | null;
583
+ ai_review_requested: boolean;
580
584
  commit_sha?: string | null | undefined;
581
585
  license?: string | null | undefined;
582
586
  circuit_json_build_error?: string | null | undefined;
@@ -616,6 +620,8 @@ declare const packageReleaseSchema: z.ZodObject<{
616
620
  circuit_json_build_completed_at?: string | null | undefined;
617
621
  circuit_json_build_logs?: any[] | undefined;
618
622
  circuit_json_build_is_stale?: boolean | undefined;
623
+ ai_review_text?: string | null | undefined;
624
+ ai_review_requested?: boolean | undefined;
619
625
  }>;
620
626
  type PackageRelease = z.infer<typeof packageReleaseSchema>;
621
627
  declare const packageFileSchema: z.ZodObject<{
@@ -939,6 +945,8 @@ declare const databaseSchema: z.ZodObject<{
939
945
  circuit_json_build_completed_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
940
946
  circuit_json_build_logs: z.ZodDefault<z.ZodArray<z.ZodAny, "many">>;
941
947
  circuit_json_build_is_stale: z.ZodDefault<z.ZodBoolean>;
948
+ ai_review_text: z.ZodDefault<z.ZodNullable<z.ZodString>>;
949
+ ai_review_requested: z.ZodDefault<z.ZodBoolean>;
942
950
  }, "strip", z.ZodTypeAny, {
943
951
  package_release_id: string;
944
952
  created_at: string;
@@ -956,6 +964,8 @@ declare const databaseSchema: z.ZodObject<{
956
964
  circuit_json_build_in_progress: boolean;
957
965
  circuit_json_build_logs: any[];
958
966
  circuit_json_build_is_stale: boolean;
967
+ ai_review_text: string | null;
968
+ ai_review_requested: boolean;
959
969
  commit_sha?: string | null | undefined;
960
970
  license?: string | null | undefined;
961
971
  circuit_json_build_error?: string | null | undefined;
@@ -995,6 +1005,8 @@ declare const databaseSchema: z.ZodObject<{
995
1005
  circuit_json_build_completed_at?: string | null | undefined;
996
1006
  circuit_json_build_logs?: any[] | undefined;
997
1007
  circuit_json_build_is_stale?: boolean | undefined;
1008
+ ai_review_text?: string | null | undefined;
1009
+ ai_review_requested?: boolean | undefined;
998
1010
  }>, "many">>;
999
1011
  packageFiles: z.ZodDefault<z.ZodArray<z.ZodObject<{
1000
1012
  package_file_id: z.ZodString;
@@ -1602,6 +1614,8 @@ declare const databaseSchema: z.ZodObject<{
1602
1614
  circuit_json_build_in_progress: boolean;
1603
1615
  circuit_json_build_logs: any[];
1604
1616
  circuit_json_build_is_stale: boolean;
1617
+ ai_review_text: string | null;
1618
+ ai_review_requested: boolean;
1605
1619
  commit_sha?: string | null | undefined;
1606
1620
  license?: string | null | undefined;
1607
1621
  circuit_json_build_error?: string | null | undefined;
@@ -1845,6 +1859,8 @@ declare const databaseSchema: z.ZodObject<{
1845
1859
  circuit_json_build_completed_at?: string | null | undefined;
1846
1860
  circuit_json_build_logs?: any[] | undefined;
1847
1861
  circuit_json_build_is_stale?: boolean | undefined;
1862
+ ai_review_text?: string | null | undefined;
1863
+ ai_review_requested?: boolean | undefined;
1848
1864
  }[] | undefined;
1849
1865
  packageFiles?: {
1850
1866
  package_release_id: string;
package/dist/schema.js CHANGED
@@ -160,7 +160,10 @@ var packageReleaseSchema = z.object({
160
160
  circuit_json_build_started_at: z.string().datetime().nullable().optional(),
161
161
  circuit_json_build_completed_at: z.string().datetime().nullable().optional(),
162
162
  circuit_json_build_logs: z.array(z.any()).default([]),
163
- circuit_json_build_is_stale: z.boolean().default(false)
163
+ circuit_json_build_is_stale: z.boolean().default(false),
164
+ // AI Review
165
+ ai_review_text: z.string().nullable().default(null),
166
+ ai_review_requested: z.boolean().default(false)
164
167
  });
165
168
  var packageFileSchema = z.object({
166
169
  package_file_id: z.string(),
@@ -201,6 +201,10 @@ export const packageReleaseSchema = z.object({
201
201
  circuit_json_build_completed_at: z.string().datetime().nullable().optional(),
202
202
  circuit_json_build_logs: z.array(z.any()).default([]),
203
203
  circuit_json_build_is_stale: z.boolean().default(false),
204
+
205
+ // AI Review
206
+ ai_review_text: z.string().nullable().default(null),
207
+ ai_review_requested: z.boolean().default(false),
204
208
  })
205
209
  export type PackageRelease = z.infer<typeof packageReleaseSchema>
206
210
 
@@ -8,6 +8,7 @@ export default withRouteSpec({
8
8
  auth: "none",
9
9
  jsonBody: z.object({
10
10
  package_id: z.string().optional(),
11
+ package_name: z.string().optional(),
11
12
  version: z.string().optional(),
12
13
  is_latest: z.boolean().optional(),
13
14
  commit_sha: z.string().optional(),
@@ -20,6 +21,7 @@ export default withRouteSpec({
20
21
  })(async (req, ctx) => {
21
22
  let {
22
23
  package_id,
24
+ package_name,
23
25
  is_latest = true,
24
26
  version,
25
27
  commit_sha,
@@ -41,10 +43,21 @@ export default withRouteSpec({
41
43
  version = parsedVersion
42
44
  }
43
45
 
46
+ if (!package_id && package_name) {
47
+ const pkg = ctx.db.packages.find((p) => p.name === package_name)
48
+ if (!pkg) {
49
+ return ctx.error(404, {
50
+ error_code: "package_not_found",
51
+ message: `Package not found: ${package_name}`,
52
+ })
53
+ }
54
+ package_id = pkg.package_id
55
+ }
56
+
44
57
  if (!package_id || !version) {
45
58
  return ctx.error(400, {
46
59
  error_code: "missing_options",
47
- message: "package_id and version are required",
60
+ message: "package_id or package_name and version are required",
48
61
  })
49
62
  }
50
63
 
@@ -12,6 +12,7 @@ export default withRouteSpec({
12
12
  license: z.string().optional(),
13
13
  fs_sha: z.string().optional(),
14
14
  ready_to_build: z.boolean().optional(),
15
+ ai_review_requested: z.boolean().optional(),
15
16
  }),
16
17
  jsonResponse: z.object({
17
18
  ok: z.boolean(),
@@ -25,6 +26,7 @@ export default withRouteSpec({
25
26
  license,
26
27
  fs_sha,
27
28
  ready_to_build,
29
+ ai_review_requested,
28
30
  } = req.jsonBody
29
31
  let releaseId = package_release_id
30
32
 
@@ -49,7 +51,14 @@ export default withRouteSpec({
49
51
  })
50
52
  }
51
53
 
52
- const delta = { is_locked, is_latest, license, fs_sha, ready_to_build }
54
+ const delta = {
55
+ is_locked,
56
+ is_latest,
57
+ license,
58
+ fs_sha,
59
+ ready_to_build,
60
+ ai_review_requested,
61
+ }
53
62
  if (
54
63
  Object.keys(delta).filter(
55
64
  (k) => delta[k as keyof typeof delta] !== undefined,
@@ -79,6 +88,10 @@ export default withRouteSpec({
79
88
  ...(license !== undefined && { license }),
80
89
  ...(fs_sha !== undefined && { fs_sha }),
81
90
  ...(ready_to_build !== undefined && { ready_to_build }),
91
+ ...(ai_review_requested !== undefined && { ai_review_requested }),
92
+ ...(ai_review_requested === true && {
93
+ ai_review_text: "Placeholder AI Review Text",
94
+ }),
82
95
  }
83
96
 
84
97
  // Handle is_latest updates atomically
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.79",
3
+ "version": "0.0.81",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -70,7 +70,7 @@
70
70
  "@radix-ui/react-toggle": "^1.1.0",
71
71
  "@radix-ui/react-toggle-group": "^1.1.0",
72
72
  "@radix-ui/react-tooltip": "^1.1.2",
73
- "@tscircuit/footprinter": "^0.0.169",
73
+ "@tscircuit/footprinter": "^0.0.176",
74
74
  "@tscircuit/layout": "^0.0.29",
75
75
  "@tscircuit/math-utils": "^0.0.10",
76
76
  "@tscircuit/mm": "^0.0.8",
@@ -126,7 +126,7 @@
126
126
  "recharts": "^2.12.7",
127
127
  "remark-gfm": "^4.0.1",
128
128
  "rollup-plugin-visualizer": "^5.12.0",
129
- "schematic-symbols": "^0.0.140",
129
+ "schematic-symbols": "^0.0.155",
130
130
  "sitemap": "^8.0.0",
131
131
  "sonner": "^1.5.0",
132
132
  "states-us": "^1.1.1",
@@ -146,7 +146,7 @@
146
146
  "@tailwindcss/typography": "^0.5.16",
147
147
  "@tscircuit/core": "^0.0.433",
148
148
  "@tscircuit/prompt-benchmarks": "^0.0.28",
149
- "@tscircuit/runframe": "^0.0.507",
149
+ "@tscircuit/runframe": "^0.0.562",
150
150
  "@types/babel__standalone": "^7.1.7",
151
151
  "@types/bun": "^1.1.10",
152
152
  "@types/country-list": "^2.1.4",
@@ -160,7 +160,7 @@
160
160
  "@typescript/vfs": "^1.6.0",
161
161
  "@vitejs/plugin-react": "^4.3.1",
162
162
  "autoprefixer": "^10.4.20",
163
- "circuit-to-svg": "^0.0.136",
163
+ "circuit-to-svg": "^0.0.152",
164
164
  "get-port": "^7.1.0",
165
165
  "globals": "^15.9.0",
166
166
  "he": "^1.2.0",
@@ -2,9 +2,14 @@ import { Github, RefreshCw } from "lucide-react"
2
2
  import { Button } from "@/components/ui/button"
3
3
  import { useParams } from "wouter"
4
4
  import { DownloadButtonAndMenu } from "../DownloadButtonAndMenu"
5
+ import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
6
+ import { useRebuildPackageReleaseMutation } from "@/hooks/use-rebuild-package-release-mutation"
5
7
 
6
8
  export function PackageBuildHeader() {
7
9
  const { author, packageName } = useParams()
10
+ const { packageRelease } = useCurrentPackageRelease()
11
+ const { mutate: rebuildPackage, isLoading } =
12
+ useRebuildPackageReleaseMutation()
8
13
 
9
14
  return (
10
15
  <div className="border-b border-gray-200 bg-white px-6 py-4">
@@ -38,9 +43,16 @@ export function PackageBuildHeader() {
38
43
  variant="outline"
39
44
  size="sm"
40
45
  className="border-gray-300 bg-white hover:bg-gray-50 text-xs sm:text-sm"
46
+ disabled={isLoading || !packageRelease}
47
+ onClick={() =>
48
+ packageRelease &&
49
+ rebuildPackage({
50
+ package_release_id: packageRelease.package_release_id,
51
+ })
52
+ }
41
53
  >
42
54
  <RefreshCw className="w-3 h-3 sm:w-4 sm:h-4 mr-1 sm:mr-2" />
43
- Rebuild
55
+ {isLoading ? "Rebuilding..." : "Rebuild"}
44
56
  </Button>
45
57
  <DownloadButtonAndMenu
46
58
  snippetUnscopedName={`${author}/${packageName}`}
@@ -23,12 +23,18 @@ interface ImportantFilesViewProps {
23
23
 
24
24
  aiDescription?: string
25
25
  aiUsageInstructions?: string
26
+ aiReviewText?: string | null
27
+ aiReviewRequested?: boolean
28
+ onRequestAiReview?: () => void
26
29
  }
27
30
 
28
31
  export default function ImportantFilesView({
29
32
  importantFiles = [],
30
33
  aiDescription,
31
34
  aiUsageInstructions,
35
+ aiReviewText,
36
+ aiReviewRequested,
37
+ onRequestAiReview,
32
38
  isLoading = false,
33
39
  onEditClicked,
34
40
  }: ImportantFilesViewProps) {
@@ -43,6 +49,7 @@ export default function ImportantFilesView({
43
49
  }
44
50
  // Determine if we have AI content
45
51
  const hasAiContent = Boolean(aiDescription || aiUsageInstructions)
52
+ const hasAiReview = Boolean(aiReviewText)
46
53
 
47
54
  // Select the appropriate tab/file when content changes
48
55
  useEffect(() => {
@@ -60,12 +67,22 @@ export default function ImportantFilesView({
60
67
  // Second priority: AI content if available
61
68
  setActiveTab("ai")
62
69
  setActiveFilePath(null)
70
+ } else if (hasAiReview) {
71
+ setActiveTab("ai-review")
72
+ setActiveFilePath(null)
63
73
  } else if (importantFiles.length > 0) {
64
74
  // Third priority: First important file
65
75
  setActiveFilePath(importantFiles[0].file_path)
66
76
  setActiveTab("file")
67
77
  }
68
- }, [aiDescription, aiUsageInstructions, hasAiContent, importantFiles])
78
+ }, [
79
+ aiDescription,
80
+ aiUsageInstructions,
81
+ aiReviewText,
82
+ hasAiContent,
83
+ hasAiReview,
84
+ importantFiles,
85
+ ])
69
86
 
70
87
  // Get file name from path
71
88
  const getFileName = (path: string) => {
@@ -106,6 +123,27 @@ export default function ImportantFilesView({
106
123
  )
107
124
  }
108
125
 
126
+ const renderAiReviewContent = () => {
127
+ if (!aiReviewText && !aiReviewRequested) {
128
+ return (
129
+ <button
130
+ className="text-sm text-blue-600 dark:text-[#58a6ff] underline"
131
+ onClick={onRequestAiReview}
132
+ >
133
+ Request AI Review
134
+ </button>
135
+ )
136
+ }
137
+
138
+ if (!aiReviewText && aiReviewRequested) {
139
+ return (
140
+ <p className="text-sm">AI review requested. Please check back later.</p>
141
+ )
142
+ }
143
+
144
+ return <MarkdownViewer markdownContent={aiReviewText || ""} />
145
+ }
146
+
109
147
  // Get active file content
110
148
  const partialActiveFile = importantFiles.find(
111
149
  (file) => file.file_path === activeFilePath,
@@ -187,6 +225,22 @@ export default function ImportantFilesView({
187
225
  </button>
188
226
  )}
189
227
 
228
+ {/* AI Review Tab */}
229
+ <button
230
+ className={`flex items-center px-3 py-1.5 rounded-md text-xs ${
231
+ activeTab === "ai-review"
232
+ ? "bg-gray-200 dark:bg-[#30363d] font-medium"
233
+ : "text-gray-500 dark:text-[#8b949e] hover:bg-gray-200 dark:hover:bg-[#30363d]"
234
+ }`}
235
+ onClick={() => {
236
+ setActiveTab("ai-review")
237
+ setActiveFilePath(null)
238
+ }}
239
+ >
240
+ <SparklesIcon className="h-3.5 w-3.5 mr-1.5" />
241
+ <span>AI Review</span>
242
+ </button>
243
+
190
244
  {/* File Tabs */}
191
245
  {importantFiles.map((file) => (
192
246
  <button
@@ -232,6 +286,8 @@ export default function ImportantFilesView({
232
286
  <div className="p-4 bg-white dark:bg-[#0d1117]">
233
287
  {activeTab === "ai" ? (
234
288
  renderAiContent()
289
+ ) : activeTab === "ai-review" ? (
290
+ renderAiReviewContent()
235
291
  ) : activeFilePath && activeFilePath.endsWith(".md") ? (
236
292
  <MarkdownViewer markdownContent={activeFileContent} />
237
293
  ) : activeFilePath &&
@@ -20,6 +20,7 @@ import { useGlobalStore } from "@/hooks/use-global-store"
20
20
  import { useLocation } from "wouter"
21
21
  import { Package } from "fake-snippets-api/lib/db/schema"
22
22
  import { useCurrentPackageCircuitJson } from "../hooks/use-current-package-circuit-json"
23
+ import { useRequestAiReviewMutation } from "@/hooks/use-request-ai-review-mutation"
23
24
 
24
25
  interface PackageFile {
25
26
  package_file_id: string
@@ -34,6 +35,7 @@ interface RepoPageContentProps {
34
35
  packageFiles?: PackageFile[]
35
36
  importantFilePaths?: string[]
36
37
  packageInfo?: Package
38
+ packageRelease?: import("fake-snippets-api/lib/db/schema").PackageRelease
37
39
  onFileClicked?: (file: PackageFile) => void
38
40
  onEditClicked?: () => void
39
41
  }
@@ -41,6 +43,7 @@ interface RepoPageContentProps {
41
43
  export default function RepoPageContent({
42
44
  packageFiles,
43
45
  packageInfo,
46
+ packageRelease,
44
47
  onFileClicked,
45
48
  onEditClicked,
46
49
  }: RepoPageContentProps) {
@@ -48,6 +51,7 @@ export default function RepoPageContent({
48
51
  const session = useGlobalStore((s) => s.session)
49
52
  const { circuitJson, isLoading: isCircuitJsonLoading } =
50
53
  useCurrentPackageCircuitJson()
54
+ const { mutate: requestAiReview } = useRequestAiReviewMutation()
51
55
 
52
56
  // Handle initial view selection and hash-based view changes
53
57
  useEffect(() => {
@@ -198,6 +202,15 @@ export default function RepoPageContent({
198
202
  onEditClicked={onEditClicked}
199
203
  aiDescription={packageInfo?.ai_description ?? ""}
200
204
  aiUsageInstructions={packageInfo?.ai_usage_instructions ?? ""}
205
+ aiReviewText={packageRelease?.ai_review_text ?? null}
206
+ aiReviewRequested={packageRelease?.ai_review_requested ?? false}
207
+ onRequestAiReview={() => {
208
+ if (packageRelease) {
209
+ requestAiReview({
210
+ package_release_id: packageRelease.package_release_id,
211
+ })
212
+ }
213
+ }}
201
214
  />
202
215
  </div>
203
216
 
@@ -17,6 +17,11 @@ import { useFileManagement } from "@/hooks/useFileManagement"
17
17
 
18
18
  interface Props {
19
19
  pkg?: Package
20
+ /**
21
+ * Optional project URL whose pathname will be used when
22
+ * reporting autorouting bugs
23
+ */
24
+ projectUrl?: string
20
25
  }
21
26
 
22
27
  export interface PackageFile {
@@ -45,7 +50,7 @@ export default () => (
45
50
  export const generateRandomPackageName = () =>
46
51
  `untitled-package-${Math.floor(Math.random() * 900) + 100}`
47
52
 
48
- export function CodeAndPreview({ pkg }: Props) {
53
+ export function CodeAndPreview({ pkg, projectUrl }: Props) {
49
54
  const { toast } = useToast()
50
55
  const urlParams = useUrlParams()
51
56
 
@@ -243,6 +248,7 @@ export function CodeAndPreview({ pkg }: Props) {
243
248
  handleEditEvent(event)
244
249
  }}
245
250
  fsMap={fsMap ?? {}}
251
+ projectUrl={projectUrl}
246
252
  />
247
253
  </div>
248
254
  )}
@@ -29,21 +29,71 @@ export const useCreatePackageReleaseMutation = ({
29
29
  )
30
30
  }
31
31
 
32
- const {
33
- data: { package_release: newPackageRelease },
34
- } = await axios.post("/package_releases/create", {
35
- package_id,
36
- version,
37
- is_latest,
38
- commit_sha,
39
- package_name_with_version,
40
- })
41
-
42
- if (!newPackageRelease) {
43
- throw new Error("Failed to create package release")
32
+ let resolvedVersion = version
33
+ let resolvedPkgName = package_name_with_version
34
+
35
+ // Parse version from package_name_with_version if needed
36
+ if (package_name_with_version && !version) {
37
+ const [pkgName, parsedVersion] = package_name_with_version.split("@")
38
+ resolvedVersion = parsedVersion
39
+ resolvedPkgName = pkgName
40
+ } else if (package_name_with_version) {
41
+ const [pkgName] = package_name_with_version.split("@")
42
+ resolvedPkgName = pkgName
43
+ }
44
+
45
+ // Default version to 1.0.0 when it contains no digits
46
+ if (!resolvedVersion || !/[0-9]/.test(resolvedVersion)) {
47
+ resolvedVersion = "1.0.0"
44
48
  }
45
49
 
46
- return newPackageRelease
50
+ const normalizedPackageNameWithVersion =
51
+ resolvedPkgName && `${resolvedPkgName}@${resolvedVersion}`
52
+
53
+ try {
54
+ const {
55
+ data: { package_release: newPackageRelease },
56
+ } = await axios.post("/package_releases/create", {
57
+ package_id,
58
+ version: resolvedVersion,
59
+ is_latest,
60
+ commit_sha,
61
+ package_name_with_version: normalizedPackageNameWithVersion,
62
+ })
63
+
64
+ if (!newPackageRelease) {
65
+ throw new Error("Failed to create package release")
66
+ }
67
+
68
+ return newPackageRelease
69
+ } catch (error: any) {
70
+ if (
71
+ error.status === 400 &&
72
+ error.data?.error?.error_code === "version_already_exists" &&
73
+ normalizedPackageNameWithVersion
74
+ ) {
75
+ // Update the existing release with the provided data
76
+ await axios.post("/package_releases/update", {
77
+ package_name_with_version: normalizedPackageNameWithVersion,
78
+ is_latest,
79
+ commit_sha,
80
+ })
81
+
82
+ const {
83
+ data: { package_release },
84
+ } = await axios.post("/package_releases/get", {
85
+ package_name_with_version: normalizedPackageNameWithVersion,
86
+ })
87
+
88
+ if (!package_release) {
89
+ throw new Error("Failed to update package release")
90
+ }
91
+
92
+ return package_release as PackageRelease
93
+ }
94
+
95
+ throw error
96
+ }
47
97
  },
48
98
  {
49
99
  onSuccess: (packageRelease: PackageRelease) => {
@@ -0,0 +1,41 @@
1
+ import { PackageRelease } from "fake-snippets-api/lib/db/schema"
2
+ import { useMutation, useQueryClient } from "react-query"
3
+ import { useAxios } from "./use-axios"
4
+ import { useToast } from "./use-toast"
5
+
6
+ export const useRebuildPackageReleaseMutation = ({
7
+ onSuccess,
8
+ }: { onSuccess?: (packageRelease: PackageRelease) => void } = {}) => {
9
+ const axios = useAxios()
10
+ const { toast } = useToast()
11
+ const queryClient = useQueryClient()
12
+
13
+ return useMutation(
14
+ async ({ package_release_id }: { package_release_id: string }) => {
15
+ const { data } = await axios.post("/package_releases/rebuild", {
16
+ package_release_id,
17
+ })
18
+ return data.package_release as PackageRelease
19
+ },
20
+ {
21
+ onSuccess: (pkgRelease) => {
22
+ toast({
23
+ title: "Rebuild triggered",
24
+ description: "The package build has been queued for rebuild.",
25
+ })
26
+ queryClient.invalidateQueries(["packageRelease"])
27
+ onSuccess?.(pkgRelease)
28
+ },
29
+ onError: (error: any) => {
30
+ toast({
31
+ title: "Error",
32
+ description:
33
+ error?.response?.data?.message ||
34
+ error?.data?.message ||
35
+ "Failed to rebuild package.",
36
+ variant: "destructive",
37
+ })
38
+ },
39
+ },
40
+ )
41
+ }
@@ -0,0 +1,45 @@
1
+ import { useMutation, useQueryClient } from "react-query"
2
+ import { useAxios } from "./use-axios"
3
+ import { useToast } from "./use-toast"
4
+ import type { PackageRelease } from "fake-snippets-api/lib/db/schema"
5
+
6
+ export const useRequestAiReviewMutation = ({
7
+ onSuccess,
8
+ }: { onSuccess?: (packageRelease: PackageRelease) => void } = {}) => {
9
+ const axios = useAxios()
10
+ const { toast } = useToast()
11
+ const queryClient = useQueryClient()
12
+
13
+ return useMutation(
14
+ async ({ package_release_id }: { package_release_id: string }) => {
15
+ await axios.post("/package_releases/update", {
16
+ package_release_id,
17
+ ai_review_requested: true,
18
+ })
19
+ const { data } = await axios.post("/package_releases/get", {
20
+ package_release_id,
21
+ })
22
+ return data.package_release as PackageRelease
23
+ },
24
+ {
25
+ onSuccess: (packageRelease) => {
26
+ toast({
27
+ title: "AI review requested",
28
+ description: "An AI review has been generated.",
29
+ })
30
+ queryClient.invalidateQueries(["packageRelease"])
31
+ onSuccess?.(packageRelease)
32
+ },
33
+ onError: (error: any) => {
34
+ toast({
35
+ title: "Error",
36
+ description:
37
+ error?.response?.data?.message ||
38
+ error?.data?.message ||
39
+ "Failed to request AI review.",
40
+ variant: "destructive",
41
+ })
42
+ },
43
+ },
44
+ )
45
+ }
@@ -65,8 +65,7 @@ export function useFileManagement({
65
65
  const initialCodeContent = useMemo(() => {
66
66
  return (
67
67
  templateCode ??
68
- decodeUrlHashToText(window.location.toString()) ??
69
- DEFAULT_CODE
68
+ (decodeUrlHashToText(window.location.toString()) || DEFAULT_CODE)
70
69
  )
71
70
  }, [templateCode, currentPackage])
72
71
  const manualEditsFileContent = useMemo(() => {