@tscircuit/fake-snippets 0.0.81 → 0.0.83

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 (37) hide show
  1. package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +3 -3
  2. package/dist/bundle.js +41 -13
  3. package/dist/index.d.ts +24 -4
  4. package/dist/index.js +5 -1
  5. package/dist/schema.d.ts +37 -5
  6. package/dist/schema.js +5 -1
  7. package/fake-snippets-api/lib/db/schema.ts +5 -1
  8. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +14 -1
  9. package/fake-snippets-api/routes/api/package_releases/get.ts +11 -3
  10. package/fake-snippets-api/routes/api/package_releases/list.ts +8 -1
  11. package/fake-snippets-api/routes/api/packages/generate_from_jlcpcb.ts +3 -3
  12. package/package.json +1 -1
  13. package/src/App.tsx +0 -2
  14. package/src/components/JLCPCBImportDialog.tsx +164 -62
  15. package/src/components/PackageBuildsPage/LogContent.tsx +12 -5
  16. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +8 -7
  17. package/src/components/PackageBuildsPage/build-preview-content.tsx +1 -1
  18. package/src/components/PackageBuildsPage/collapsible-section.tsx +14 -46
  19. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +28 -10
  20. package/src/components/PackageBuildsPage/package-build-header.tsx +16 -4
  21. package/src/components/ViewPackagePage/components/build-status.tsx +24 -85
  22. package/src/components/ViewPackagePage/components/important-files-view.tsx +8 -1
  23. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +28 -5
  24. package/src/components/ViewPackagePage/hooks/use-toast.tsx +70 -0
  25. package/src/components/dialogs/{import-snippet-dialog.tsx → import-package-dialog.tsx} +25 -24
  26. package/src/components/package-port/CodeEditor.tsx +9 -1
  27. package/src/components/package-port/CodeEditorHeader.tsx +7 -6
  28. package/src/components/ui/toaster.tsx +1 -33
  29. package/src/hooks/use-current-package-release.ts +14 -3
  30. package/src/hooks/use-now.ts +12 -0
  31. package/src/hooks/use-package-release.ts +17 -15
  32. package/src/hooks/use-toast.tsx +50 -169
  33. package/src/pages/dashboard.tsx +3 -1
  34. package/src/pages/user-profile.tsx +9 -2
  35. package/src/pages/view-package.tsx +1 -0
  36. package/.github/workflows/formatbot.yml +0 -63
  37. package/src/components/ViewPackagePage/hooks/use-toast.ts +0 -191
@@ -1,99 +1,38 @@
1
- import { useState } from "react"
2
- import {
3
- Dialog,
4
- DialogContent,
5
- DialogHeader,
6
- DialogTitle,
7
- } from "@/components/ui/dialog"
8
- import { CheckCircle, XCircle, Check, X } from "lucide-react"
9
- import { cn } from "@/lib/utils"
1
+ import { CheckCircle, XCircle, Clock, Loader2 } from "lucide-react"
2
+ import { Link, useParams } from "wouter"
10
3
 
11
4
  export interface BuildStep {
12
5
  id: string
13
6
  name: string
14
- status: "success" | "failed"
15
- message?: string
7
+ status: "pending" | "running" | "success" | "error"
16
8
  }
17
9
 
18
10
  export interface BuildStatusProps {
19
11
  step: BuildStep
12
+ packageReleaseId: string
20
13
  }
21
14
 
22
- export const BuildStatus = ({ step }: BuildStatusProps) => {
23
- const [isDialogOpen, setIsDialogOpen] = useState(false)
15
+ export const BuildStatus = ({ step, packageReleaseId }: BuildStatusProps) => {
16
+ const { author, packageName } = useParams()
17
+ const href = `/${author}/${packageName}/builds?package_release_id=${packageReleaseId}`
24
18
 
25
19
  return (
26
- <>
27
- <div
28
- onClick={() => setIsDialogOpen(true)}
29
- className={"flex items-center cursor-pointer"}
30
- >
31
- {step.status === "success" ? (
32
- <CheckCircle className="h-4 w-4 mr-2 text-green-600 dark:text-[#8b949e]" />
33
- ) : (
34
- <XCircle className="h-4 w-4 mr-2 text-red-600 dark:text-[#8b949e]" />
35
- )}
36
- <span className="text-sm text-gray-500 dark:text-[#8b949e]">
37
- {step.name}
38
- </span>
39
- </div>
40
-
41
- <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
42
- <DialogContent className="sm:max-w-md">
43
- <DialogHeader>
44
- <DialogTitle className="flex items-center gap-2">
45
- {step.status === "success" ? (
46
- <>
47
- <CheckCircle className="h-5 w-5 text-green-600" />
48
- <span>Build Status: Passing</span>
49
- </>
50
- ) : (
51
- <>
52
- <XCircle className="h-5 w-5 text-red-600" />
53
- <span>Build Status: Failing</span>
54
- </>
55
- )}
56
- </DialogTitle>
57
- </DialogHeader>
58
-
59
- <div className="space-y-4">
60
- <div className="space-y-3">
61
- <div
62
- key={step.id}
63
- className={cn(
64
- "flex items-start gap-3 rounded-md border p-3",
65
- step.status === "success"
66
- ? "bg-green-50 border-green-200"
67
- : "bg-red-50 border-red-200",
68
- )}
69
- >
70
- <div
71
- className={cn(
72
- "rounded-full p-1 mt-0.5",
73
- step.status === "success"
74
- ? "bg-green-100 text-green-600"
75
- : "bg-red-100 text-red-600",
76
- )}
77
- >
78
- {step.status === "success" ? (
79
- <Check className="h-4 w-4" />
80
- ) : (
81
- <X className="h-4 w-4" />
82
- )}
83
- </div>
84
- <div>
85
- <div className="font-medium">{step.name}</div>
86
- {step.message && (
87
- <div className="text-sm text-muted-foreground mt-1">
88
- {step.message}
89
- </div>
90
- )}
91
- </div>
92
- </div>
93
- </div>
94
- </div>
95
- </DialogContent>
96
- </Dialog>
97
- </>
20
+ <Link href={href} className="flex items-center gap-2">
21
+ {step.status === "success" && (
22
+ <CheckCircle className="h-4 w-4 text-green-600 dark:text-[#8b949e]" />
23
+ )}
24
+ {step.status === "error" && (
25
+ <XCircle className="h-4 w-4 text-red-600 dark:text-[#8b949e]" />
26
+ )}
27
+ {step.status === "running" && (
28
+ <Loader2 className="h-4 w-4 text-blue-600 animate-spin dark:text-[#8b949e]" />
29
+ )}
30
+ {step.status === "pending" && (
31
+ <Clock className="h-4 w-4 text-yellow-600 dark:text-[#8b949e]" />
32
+ )}
33
+ <span className="text-sm text-gray-500 dark:text-[#8b949e]">
34
+ {step.name}
35
+ </span>
36
+ </Link>
98
37
  )
99
38
  }
@@ -51,8 +51,13 @@ export default function ImportantFilesView({
51
51
  const hasAiContent = Boolean(aiDescription || aiUsageInstructions)
52
52
  const hasAiReview = Boolean(aiReviewText)
53
53
 
54
- // Select the appropriate tab/file when content changes
54
+ // Select the appropriate tab/file when content changes. Once the user has
55
+ // interacted with the tabs we keep their selection and only run this logic
56
+ // if no tab has been chosen yet.
55
57
  useEffect(() => {
58
+ if (activeTab !== null) return
59
+ if (isLoading) return
60
+
56
61
  // First priority: README file if it exists
57
62
  const readmeFile = importantFiles.find(
58
63
  (file) =>
@@ -82,6 +87,8 @@ export default function ImportantFilesView({
82
87
  hasAiContent,
83
88
  hasAiReview,
84
89
  importantFiles,
90
+ activeTab,
91
+ isLoading,
85
92
  ])
86
93
 
87
94
  // Get file name from path
@@ -4,6 +4,27 @@ import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
4
4
  import { usePackageReleaseById } from "@/hooks/use-package-release"
5
5
  import { timeAgo } from "@/lib/utils/timeAgo"
6
6
  import { BuildStatus, BuildStep } from "./build-status"
7
+ import type { PackageRelease } from "fake-snippets-api/lib/db/schema"
8
+
9
+ function getTranspilationStatus(
10
+ pr?: PackageRelease | null,
11
+ ): BuildStep["status"] {
12
+ if (!pr) return "pending"
13
+ if (pr.transpilation_error) return "error"
14
+ if (pr.transpilation_in_progress) return "running"
15
+ if (pr.transpilation_completed_at) return "success"
16
+ if (pr.transpilation_started_at) return "running"
17
+ return "pending"
18
+ }
19
+
20
+ function getCircuitJsonStatus(pr?: PackageRelease | null): BuildStep["status"] {
21
+ if (!pr) return "pending"
22
+ if (pr.circuit_json_build_error) return "error"
23
+ if (pr.circuit_json_build_in_progress) return "running"
24
+ if (pr.circuit_json_build_completed_at) return "success"
25
+ if (pr.circuit_json_build_started_at) return "running"
26
+ return "pending"
27
+ }
7
28
 
8
29
  export default function SidebarReleasesSection() {
9
30
  const { packageInfo } = useCurrentPackageInfo()
@@ -15,14 +36,12 @@ export default function SidebarReleasesSection() {
15
36
  {
16
37
  id: "package_transpilation",
17
38
  name: "Package Transpilation",
18
- status: packageRelease?.has_transpiled ? "success" : "failed",
19
- message: packageRelease?.transpilation_error || undefined,
39
+ status: getTranspilationStatus(packageRelease),
20
40
  },
21
41
  {
22
42
  id: "circuit_json_build",
23
43
  name: "Circuit JSON Build",
24
- status: packageRelease?.circuit_json_build_error ? "failed" : "success",
25
- message: packageRelease?.circuit_json_build_error || undefined,
44
+ status: getCircuitJsonStatus(packageRelease),
26
45
  },
27
46
  ]
28
47
 
@@ -56,7 +75,11 @@ export default function SidebarReleasesSection() {
56
75
  </span>
57
76
  </div>
58
77
  {buildSteps.map((step) => (
59
- <BuildStatus key={step.id} step={step} />
78
+ <BuildStatus
79
+ key={step.id}
80
+ step={step}
81
+ packageReleaseId={packageRelease.package_release_id}
82
+ />
60
83
  ))}
61
84
  </div>
62
85
  {/* <a href="#" className="text-blue-600 dark:text-[#58a6ff] hover:underline text-sm">
@@ -0,0 +1,70 @@
1
+ "use client"
2
+ import toastLibrary, { Toaster, type Toast } from "react-hot-toast"
3
+ import React from "react"
4
+
5
+ export interface ToasterToast {
6
+ title?: React.ReactNode
7
+ description?: React.ReactNode
8
+ variant?: "default" | "destructive"
9
+ duration?: number
10
+ }
11
+
12
+ function ToastContent({
13
+ title,
14
+ description,
15
+ variant,
16
+ t,
17
+ }: ToasterToast & { t: Toast }) {
18
+ return (
19
+ <div
20
+ className={`rounded-md border p-4 shadow-lg transition-all ${
21
+ t.visible
22
+ ? "animate-in fade-in slide-in-from-top-full"
23
+ : "animate-out fade-out slide-out-to-right-full"
24
+ } ${
25
+ variant === "destructive"
26
+ ? "border-red-500 bg-red-500 text-slate-50"
27
+ : "border-slate-200 bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50"
28
+ }`}
29
+ >
30
+ {title && <div className="text-sm font-semibold">{title}</div>}
31
+ {description && <div className="text-sm opacity-90">{description}</div>}
32
+ </div>
33
+ )
34
+ }
35
+
36
+ const toast = ({
37
+ duration,
38
+ description,
39
+ variant = "default",
40
+ title,
41
+ }: ToasterToast) => {
42
+ if (description) {
43
+ return toastLibrary.custom(
44
+ (t) => (
45
+ <ToastContent
46
+ title={title}
47
+ description={description}
48
+ variant={variant}
49
+ t={t}
50
+ />
51
+ ),
52
+ { duration },
53
+ )
54
+ }
55
+
56
+ if (variant === "destructive") {
57
+ return toastLibrary.error(<>{title}</>, { duration })
58
+ }
59
+
60
+ return toastLibrary(<>{title}</>, { duration })
61
+ }
62
+
63
+ function useToast() {
64
+ return {
65
+ toast,
66
+ dismiss: toastLibrary.dismiss,
67
+ }
68
+ }
69
+
70
+ export { useToast, toast, Toaster }
@@ -1,6 +1,6 @@
1
1
  import { useAxios } from "@/hooks/use-axios"
2
2
  import { useDebounce } from "@/hooks/use-debounce"
3
- import type { Snippet } from "fake-snippets-api/lib/db/schema"
3
+ import type { Package } from "fake-snippets-api/lib/db/schema"
4
4
  import { useState } from "react"
5
5
  import { useQuery } from "react-query"
6
6
  import { Button } from "../ui/button"
@@ -8,25 +8,25 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
8
8
  import { Input } from "../ui/input"
9
9
  import { createUseDialog } from "./create-use-dialog"
10
10
 
11
- export const ImportSnippetDialog = ({
11
+ export const ImportPackageDialog = ({
12
12
  open,
13
13
  onOpenChange,
14
- onSnippetSelected,
14
+ onPackageSelected,
15
15
  }: {
16
16
  open: boolean
17
17
  onOpenChange: (open: boolean) => any
18
- onSnippetSelected: (snippet: Snippet) => any
18
+ onPackageSelected: (pkg: Package) => any
19
19
  }) => {
20
20
  const [searchText, setSearchText] = useState("")
21
21
  const debouncedSearch = useDebounce(searchText, 300)
22
22
  const axios = useAxios()
23
23
  const { data: snippets, isLoading } = useQuery(
24
- ["snippetSearch", debouncedSearch],
24
+ ["packageSearch", debouncedSearch],
25
25
  async () => {
26
- const response = await axios.get(
27
- `/snippets/search?q=${encodeURIComponent(debouncedSearch)}`,
28
- )
29
- return response.data.snippets.slice(0, 12)
26
+ const response = await axios.post("/packages/search", {
27
+ query: debouncedSearch,
28
+ })
29
+ return response.data.packages
30
30
  },
31
31
  {
32
32
  enabled: debouncedSearch.length > 0,
@@ -35,41 +35,42 @@ export const ImportSnippetDialog = ({
35
35
 
36
36
  return (
37
37
  <Dialog open={open} onOpenChange={onOpenChange}>
38
- <DialogContent className="z-[100]">
38
+ <DialogContent className="z-[100] p-4 sm:p-6">
39
39
  <DialogHeader>
40
- <DialogTitle>Import Snippet</DialogTitle>
40
+ <DialogTitle>Import Package</DialogTitle>
41
41
  </DialogHeader>
42
42
  <Input
43
- placeholder="Search snippets..."
43
+ placeholder="Search packages..."
44
44
  value={searchText}
45
45
  onChange={(e) => setSearchText(e.target.value)}
46
+ className="w-full mb-4"
46
47
  />
47
48
  <div className="h-64 overflow-y-auto">
48
49
  {isLoading ? (
49
- <div>Loading...</div>
50
+ <div className="text-center">Loading...</div>
50
51
  ) : (
51
52
  <ul className="w-full">
52
- {snippets?.map((snippet: Snippet) => (
53
+ {snippets?.map((pkg: Package) => (
53
54
  <li
54
- className="flex items-center my-1 text-xs w-full"
55
- key={snippet.snippet_id}
55
+ className="flex flex-col sm:flex-row items-start sm:items-center my-2 text-sm w-full"
56
+ key={pkg.package_id}
56
57
  >
57
58
  <a
58
- href={`/${snippet.name}`}
59
+ href={`/${pkg.name}`}
59
60
  target="_blank"
60
- className="whitespace-nowrap mr-2 text-blue-500 hover:underline cursor-pointer flex-shrink-0"
61
+ className="text-blue-500 hover:underline cursor-pointer flex-shrink-0 mb-1 sm:mb-0 sm:mr-2"
61
62
  >
62
- {snippet.name}
63
+ {pkg.name}
63
64
  </a>
64
- <div className="text-xs text-gray-500 flex-grow overflow-hidden text-ellipsis whitespace-nowrap">
65
- {snippet.description}
65
+ <div className="text-gray-500 flex-grow overflow-hidden text-ellipsis whitespace-nowrap mb-1 sm:mb-0">
66
+ {pkg.description}
66
67
  </div>
67
68
  <Button
68
69
  size="sm"
69
- className="ml-2 flex-shrink-0"
70
+ className="flex-shrink-0"
70
71
  variant="outline"
71
72
  onClick={() => {
72
- onSnippetSelected(snippet)
73
+ onPackageSelected(pkg)
73
74
  onOpenChange(false)
74
75
  }}
75
76
  >
@@ -85,4 +86,4 @@ export const ImportSnippetDialog = ({
85
86
  )
86
87
  }
87
88
 
88
- export const useImportSnippetDialog = createUseDialog(ImportSnippetDialog)
89
+ export const useImportPackageDialog = createUseDialog(ImportPackageDialog)
@@ -4,7 +4,7 @@ import { autocompletion } from "@codemirror/autocomplete"
4
4
  import { indentWithTab } from "@codemirror/commands"
5
5
  import { javascript } from "@codemirror/lang-javascript"
6
6
  import { json } from "@codemirror/lang-json"
7
- import { EditorState } from "@codemirror/state"
7
+ import { EditorState, Prec } from "@codemirror/state"
8
8
  import { Decoration, hoverTooltip, keymap } from "@codemirror/view"
9
9
  import { getImportsFromCode } from "@tscircuit/prompt-benchmarks/code-runner-utils"
10
10
  import type { ATABootstrapConfig } from "@typescript/ata"
@@ -237,6 +237,14 @@ export const CodeEditor = ({
237
237
  currentFile?.endsWith(".json")
238
238
  ? json()
239
239
  : javascript({ typescript: true, jsx: true }),
240
+ Prec.high(
241
+ keymap.of([
242
+ {
243
+ key: "Mod-Enter",
244
+ run: () => true,
245
+ },
246
+ ]),
247
+ ),
240
248
  keymap.of([indentWithTab]),
241
249
  EditorState.readOnly.of(readOnly || isSaving),
242
250
  EditorView.updateListener.of((update) => {
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useCallback } from "react"
2
2
  import { Button } from "@/components/ui/button"
3
3
  import { handleManualEditsImportWithSupportForMultipleFiles } from "@/lib/handleManualEditsImportWithSupportForMultipleFiles"
4
- import { useImportSnippetDialog } from "@/components/dialogs/import-snippet-dialog"
4
+ import { useImportPackageDialog } from "@/components/dialogs/import-package-dialog"
5
5
  import { useToast } from "@/hooks/use-toast"
6
6
  import {
7
7
  DropdownMenu,
@@ -19,6 +19,7 @@ import {
19
19
  SelectValue,
20
20
  } from "../ui/select"
21
21
  import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
22
+ import { Package } from "fake-snippets-api/lib/db/schema"
22
23
 
23
24
  export type FileName = string
24
25
 
@@ -39,8 +40,8 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
39
40
  handleFileChange,
40
41
  entrypointFileName = "index.tsx",
41
42
  }) => {
42
- const { Dialog: ImportSnippetDialog, openDialog: openImportDialog } =
43
- useImportSnippetDialog()
43
+ const { Dialog: ImportPackageDialog, openDialog: openImportDialog } =
44
+ useImportPackageDialog()
44
45
  const { toast } = useToast()
45
46
  const [sidebarOpen, setSidebarOpen] = fileSidebarState
46
47
 
@@ -234,9 +235,9 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
234
235
  Format
235
236
  </Button>
236
237
  </div>
237
- <ImportSnippetDialog
238
- onSnippetSelected={(snippet: any) => {
239
- const newContent = `import {} from "@tsci/${snippet.owner_name}.${snippet.unscoped_name}"\n${files[currentFile || ""]}`
238
+ <ImportPackageDialog
239
+ onPackageSelected={(pkg: Package) => {
240
+ const newContent = `import {} from "@tsci/${pkg.owner_github_username}.${pkg.unscoped_name}"\n${files[currentFile || ""]}`
240
241
  updateFileContent(currentFile, newContent)
241
242
  }}
242
243
  />
@@ -1,33 +1 @@
1
- import { useToast } from "@/hooks/use-toast"
2
- import {
3
- Toast,
4
- ToastClose,
5
- ToastDescription,
6
- ToastProvider,
7
- ToastTitle,
8
- ToastViewport,
9
- } from "@/components/ui/toast"
10
-
11
- export function Toaster() {
12
- const { toasts } = useToast()
13
-
14
- return (
15
- <ToastProvider>
16
- {toasts.map(function ({ id, title, description, action, ...props }) {
17
- return (
18
- <Toast key={id} {...props}>
19
- <div className="grid gap-1">
20
- {title && <ToastTitle>{title}</ToastTitle>}
21
- {description && (
22
- <ToastDescription>{description}</ToastDescription>
23
- )}
24
- </div>
25
- {action}
26
- <ToastClose />
27
- </Toast>
28
- )
29
- })}
30
- <ToastViewport />
31
- </ToastProvider>
32
- )
33
- }
1
+ export { Toaster } from "react-hot-toast"
@@ -1,10 +1,12 @@
1
1
  import { useParams } from "wouter"
2
2
  import { useCurrentPackageId } from "./use-current-package-id"
3
- import { useUrlParams } from "./use-url-params"
4
3
  import { usePackageRelease } from "./use-package-release"
4
+ import { useUrlParams } from "./use-url-params"
5
5
 
6
6
  export const useCurrentPackageRelease = (options?: {
7
- include_logs: boolean
7
+ include_ai_review?: boolean
8
+ include_logs?: boolean
9
+ refetchInterval?: number
8
10
  }) => {
9
11
  const { packageId } = useCurrentPackageId()
10
12
  const urlParams = useUrlParams()
@@ -25,8 +27,17 @@ export const useCurrentPackageRelease = (options?: {
25
27
  query = { package_id: packageId, is_latest: true }
26
28
  }
27
29
 
30
+ if (query && options?.include_logs !== undefined) {
31
+ query.include_logs = options.include_logs
32
+ }
33
+
34
+ if (query && options?.include_ai_review !== undefined) {
35
+ query.include_ai_review = options.include_ai_review
36
+ }
37
+
28
38
  const { data: packageRelease, ...rest } = usePackageRelease(query, {
29
- include_logs: options?.include_logs ?? false,
39
+ refetchInterval: options?.refetchInterval,
30
40
  })
41
+
31
42
  return { packageRelease, ...rest }
32
43
  }
@@ -0,0 +1,12 @@
1
+ import { useEffect, useState } from "react"
2
+
3
+ export const useNow = (intervalMs: number = 1000) => {
4
+ const [now, setNow] = useState(Date.now())
5
+
6
+ useEffect(() => {
7
+ const id = setInterval(() => setNow(Date.now()), intervalMs)
8
+ return () => clearInterval(id)
9
+ }, [intervalMs])
10
+
11
+ return now
12
+ }
@@ -1,8 +1,8 @@
1
1
  import type { PackageRelease } from "fake-snippets-api/lib/db/schema"
2
- import { useQuery } from "react-query"
2
+ import { type UseQueryOptions, useQuery } from "react-query"
3
3
  import { useAxios } from "./use-axios"
4
4
 
5
- type PackageReleaseQuery =
5
+ type PackageReleaseQuery = (
6
6
  | {
7
7
  package_release_id: string
8
8
  }
@@ -17,29 +17,30 @@ type PackageReleaseQuery =
17
17
  package_id: string
18
18
  is_latest: boolean
19
19
  }
20
+ ) & {
21
+ include_logs?: boolean | null | undefined
22
+ include_ai_review?: boolean | null | undefined
23
+ }
20
24
 
21
25
  export const usePackageRelease = (
22
26
  query: PackageReleaseQuery | null,
23
- options?: { include_logs: boolean },
27
+ options?: {
28
+ refetchInterval?: number
29
+ },
24
30
  ) => {
25
31
  const axios = useAxios()
26
32
 
27
33
  return useQuery<PackageRelease, Error & { status: number }>(
28
- ["packageRelease", query, options?.include_logs],
34
+ ["packageRelease", query],
29
35
  async () => {
30
36
  if (!query) return
31
37
 
32
- const { data } = await axios.post(
33
- "/package_releases/get",
34
- query,
35
- options?.include_logs
36
- ? {
37
- params: {
38
- include_logs: true,
39
- },
40
- }
41
- : undefined,
42
- )
38
+ const { data } = await axios.post("/package_releases/get", query, {
39
+ params: {
40
+ include_logs: query.include_logs,
41
+ include_ai_review: query.include_ai_review,
42
+ },
43
+ })
43
44
 
44
45
  if (!data.package_release) {
45
46
  throw new Error("Package release not found")
@@ -50,6 +51,7 @@ export const usePackageRelease = (
50
51
  {
51
52
  retry: false,
52
53
  enabled: Boolean(query),
54
+ refetchInterval: options?.refetchInterval,
53
55
  },
54
56
  )
55
57
  }