@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/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +31 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +3 -0
- package/bun.lock +24 -18
- package/dist/bundle.js +35 -7
- package/dist/index.d.ts +10 -0
- package/dist/index.js +4 -1
- package/dist/schema.d.ts +16 -0
- package/dist/schema.js +4 -1
- package/fake-snippets-api/lib/db/schema.ts +4 -0
- package/fake-snippets-api/routes/api/package_releases/create.ts +14 -1
- package/fake-snippets-api/routes/api/package_releases/update.ts +14 -1
- package/package.json +5 -5
- package/src/components/PackageBuildsPage/package-build-header.tsx +13 -1
- package/src/components/ViewPackagePage/components/important-files-view.tsx +57 -1
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +13 -0
- package/src/components/package-port/CodeAndPreview.tsx +7 -1
- package/src/hooks/use-create-package-release-mutation.ts +63 -13
- package/src/hooks/use-rebuild-package-release-mutation.ts +41 -0
- package/src/hooks/use-request-ai-review-mutation.ts +45 -0
- package/src/hooks/useFileManagement.ts +1 -2
- package/src/lib/codemirror/basic-setup.ts +17 -1
- package/src/lib/templates/blank-package-template.ts +4 -5
- package/src/pages/editor.tsx +5 -1
- package/src/pages/view-package.tsx +1 -0
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 = {
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
}, [
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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(() => {
|