@tscircuit/fake-snippets 0.0.103 → 0.0.105

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.
@@ -1,17 +1,24 @@
1
- import { useRef } from "react"
1
+ import { useRef, useState, useMemo } from "react"
2
+ import { Check, ChevronsUpDown } from "lucide-react"
2
3
  import {
3
- Select,
4
- SelectContent,
5
- SelectItem,
6
- SelectTrigger,
7
- SelectValue,
8
- } from "@/components/ui/select"
4
+ Command,
5
+ CommandEmpty,
6
+ CommandGroup,
7
+ CommandInput,
8
+ CommandItem,
9
+ } from "@/components/ui/command"
10
+ import {
11
+ Popover,
12
+ PopoverContent,
13
+ PopoverTrigger,
14
+ } from "@/components/ui/popover"
15
+ import { cn } from "@/lib/utils"
9
16
  import { useAxios } from "@/hooks/use-axios"
10
17
  import { useApiBaseUrl } from "@/hooks/use-packages-base-api-url"
11
18
  import { useQuery } from "react-query"
12
19
  import { Button } from "../ui/button"
13
20
  import { Label } from "../ui/label"
14
- import { Minus, Plus } from "lucide-react"
21
+ import { Minus, Plus, RefreshCw } from "lucide-react"
15
22
  import { Switch } from "../ui/switch"
16
23
 
17
24
  interface GitHubRepositorySelectorProps {
@@ -19,9 +26,8 @@ interface GitHubRepositorySelectorProps {
19
26
  setSelectedRepository?: (value: string | null) => void
20
27
  disabled?: boolean
21
28
  open?: boolean
22
- addFormContent?: (data: {
23
- enablePrPreview?: boolean
24
- privateBuild?: boolean
29
+ addFormContent?: (props: {
30
+ allowPrPreviews?: boolean
25
31
  }) => void
26
32
  formData?: any
27
33
  }
@@ -37,8 +43,15 @@ export const GitHubRepositorySelector = ({
37
43
  const axios = useAxios()
38
44
  const apiBaseUrl = useApiBaseUrl()
39
45
  const initialValue = useRef(selectedRepository).current
46
+ const [comboboxOpen, setComboboxOpen] = useState(false)
47
+ const [searchValue, setSearchValue] = useState("")
40
48
  // Fetch available repositories
41
- const { data: repositoriesData, error: repositoriesError } = useQuery(
49
+ const {
50
+ data: repositoriesData,
51
+ error: repositoriesError,
52
+ refetch: refetchRepositories,
53
+ isLoading,
54
+ } = useQuery(
42
55
  ["github-repositories"],
43
56
  async () => {
44
57
  const response = await axios.get("/github/repos/list_available")
@@ -54,20 +67,103 @@ export const GitHubRepositorySelector = ({
54
67
  window.location.href = `${apiBaseUrl}/github/installations/create_new_installation_redirect?return_to_page=${window.location.pathname}`
55
68
  }
56
69
 
57
- const handleValueChange = (newValue: string) => {
58
- if (newValue === "connect-more") {
70
+ const handleRefreshRepositories = async () => {
71
+ try {
72
+ // First call the refresh endpoint to update repositories
73
+ await axios.post("/github/repos/refresh")
74
+ // Then refetch the repositories list
75
+ refetchRepositories()
76
+ } catch (error) {
77
+ console.error("Failed to refresh repositories:", error)
78
+ // Still try to refetch in case the error is not critical
79
+ refetchRepositories()
80
+ }
81
+ }
82
+
83
+ // Create searchable options for the combobox
84
+ const comboboxOptions = useMemo(() => {
85
+ const repos = repositoriesData?.repos || []
86
+ const repoOptions = repos.map((repo: any) => ({
87
+ value: repo.full_name,
88
+ label: repo.unscoped_name,
89
+ isPrivate: repo.private,
90
+ type: "repo" as const,
91
+ }))
92
+
93
+ const specialOptions = [
94
+ {
95
+ value: "connect-more",
96
+ label: "Connect More Repos",
97
+ type: "special" as const,
98
+ icon: "plus" as const,
99
+ },
100
+ ...(initialValue
101
+ ? [
102
+ {
103
+ value: "unlink//repo",
104
+ label: "Unlink Repo",
105
+ type: "special" as const,
106
+ icon: "minus" as const,
107
+ },
108
+ ]
109
+ : []),
110
+ ]
111
+
112
+ return [...repoOptions, ...specialOptions]
113
+ }, [repositoriesData?.repos, initialValue])
114
+
115
+ // Filter options based on search
116
+ const filteredOptions = useMemo(() => {
117
+ if (!searchValue) return comboboxOptions
118
+ return comboboxOptions.filter(
119
+ (option) =>
120
+ option.label.toLowerCase().includes(searchValue.toLowerCase()) ||
121
+ option.value.toLowerCase().includes(searchValue.toLowerCase()),
122
+ )
123
+ }, [comboboxOptions, searchValue])
124
+
125
+ const handleComboboxSelect = (value: string) => {
126
+ if (value === "connect-more") {
59
127
  handleConnectMoreRepos()
60
- } else if (newValue === "unlink//repo") {
61
- setSelectedRepository?.("unlink//repo")
62
128
  } else {
63
- setSelectedRepository?.(newValue)
129
+ setSelectedRepository?.(value)
64
130
  }
131
+ setComboboxOpen(false)
132
+ setSearchValue("")
133
+ }
134
+
135
+ const getDisplayValue = () => {
136
+ if (!selectedRepository) return "Select a repository"
137
+ const option = comboboxOptions.find(
138
+ (opt) => opt.value === selectedRepository,
139
+ )
140
+ return option?.label || selectedRepository
65
141
  }
66
142
 
67
143
  return (
68
144
  <>
69
145
  <div className="space-y-1 mb-3">
70
- <Label htmlFor="repository">GitHub Repository</Label>
146
+ <div className="flex items-center justify-between">
147
+ <Label htmlFor="repository">GitHub Repository</Label>
148
+ {!(
149
+ (repositoriesError as any)?.response?.status === 400 &&
150
+ (repositoriesError as any)?.response?.data?.error_code ===
151
+ "github_not_connected"
152
+ ) && (
153
+ <Button
154
+ type="button"
155
+ variant="ghost"
156
+ size="sm"
157
+ onClick={handleRefreshRepositories}
158
+ disabled={disabled || isLoading}
159
+ className="h-auto p-1"
160
+ >
161
+ <RefreshCw
162
+ className={`w-3 h-3 ${isLoading ? "animate-spin" : ""}`}
163
+ />
164
+ </Button>
165
+ )}
166
+ </div>
71
167
  {(repositoriesError as any)?.response?.status === 400 &&
72
168
  (repositoriesError as any)?.response?.data?.error_code ===
73
169
  "github_not_connected" ? (
@@ -94,71 +190,85 @@ export const GitHubRepositorySelector = ({
94
190
  </div>
95
191
  ) : (
96
192
  <div className="space-y-2">
97
- <Select
98
- value={selectedRepository}
99
- onValueChange={handleValueChange}
100
- disabled={disabled}
101
- >
102
- <SelectTrigger className="w-full">
103
- <SelectValue placeholder="Select a repository" />
104
- </SelectTrigger>
105
- <SelectContent className="!z-[999]">
106
- {repositoriesData?.repos?.map((repo: any) => (
107
- <SelectItem key={repo.full_name} value={repo.full_name}>
108
- <div className="flex items-center space-x-2">
109
- <span>{repo.unscoped_name}</span>
110
- {repo.private && (
111
- <span className="text-xs text-muted-foreground">
112
- (private)
113
- </span>
114
- )}
115
- </div>
116
- </SelectItem>
117
- ))}
118
- <SelectItem value="connect-more">
119
- <div className="flex items-center space-x-2 text-blue-600">
120
- <Plus className="w-3 h-3" />
121
- <span>Connect More Repos</span>
122
- </div>
123
- </SelectItem>
124
- {Boolean(initialValue) && (
125
- <SelectItem value="unlink//repo">
126
- <div className="flex items-center space-x-2 text-red-600">
127
- <Minus className="w-3 h-3" />
128
- <span>Unlink Repo</span>
129
- </div>
130
- </SelectItem>
131
- )}
132
- </SelectContent>
133
- </Select>
193
+ <Popover open={comboboxOpen} onOpenChange={setComboboxOpen}>
194
+ <PopoverTrigger asChild>
195
+ <Button
196
+ variant="outline"
197
+ role="combobox"
198
+ aria-expanded={comboboxOpen}
199
+ className="w-full justify-between"
200
+ disabled={disabled}
201
+ >
202
+ {getDisplayValue()}
203
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
204
+ </Button>
205
+ </PopoverTrigger>
206
+ <PopoverContent className="w-full p-0 z-[999]">
207
+ <Command shouldFilter={false}>
208
+ <CommandInput
209
+ value={searchValue}
210
+ onValueChange={setSearchValue}
211
+ placeholder="Search repositories..."
212
+ />
213
+ <CommandEmpty className="text-sm text-slate-500 py-6">
214
+ No repositories found.
215
+ </CommandEmpty>
216
+ <CommandGroup className="max-h-[400px] overflow-y-auto">
217
+ {filteredOptions.map((option) => (
218
+ <CommandItem
219
+ key={option.value}
220
+ onSelect={() => handleComboboxSelect(option.value)}
221
+ className="cursor-pointer"
222
+ >
223
+ <div className="flex items-center space-x-2 w-full">
224
+ {option.type === "repo" ? (
225
+ <>
226
+ <Check
227
+ className={cn(
228
+ "mr-2 h-4 w-4",
229
+ selectedRepository === option.value
230
+ ? "opacity-100"
231
+ : "opacity-0",
232
+ )}
233
+ />
234
+ <span>{option.label}</span>
235
+ {option.isPrivate && (
236
+ <span className="text-xs text-muted-foreground">
237
+ (private)
238
+ </span>
239
+ )}
240
+ </>
241
+ ) : (
242
+ <>
243
+ {option.icon === "plus" ? (
244
+ <Plus className="w-3 h-3 text-blue-600" />
245
+ ) : (
246
+ <Minus className="w-3 h-3 text-red-600" />
247
+ )}
248
+ <span
249
+ className={
250
+ option.icon === "plus"
251
+ ? "text-blue-600"
252
+ : "text-red-600"
253
+ }
254
+ >
255
+ {option.label}
256
+ </span>
257
+ </>
258
+ )}
259
+ </div>
260
+ </CommandItem>
261
+ ))}
262
+ </CommandGroup>
263
+ </Command>
264
+ </PopoverContent>
265
+ </Popover>
134
266
  </div>
135
267
  )}
136
268
  </div>
137
269
 
138
270
  {initialValue && selectedRepository !== "unlink//repo" && (
139
271
  <div className="space-y-4 mt-4 p-4 border rounded-lg bg-gray-50">
140
- <h4 className="text-sm font-medium text-gray-900">
141
- Repository Settings
142
- </h4>
143
-
144
- <div className="flex items-center justify-between">
145
- <div className="space-y-0.5">
146
- <Label className="text-sm font-medium">Private Build</Label>
147
- <p className="text-xs text-gray-500">
148
- Keep build previews private
149
- </p>
150
- </div>
151
- <Switch
152
- checked={formData?.privateBuild}
153
- onCheckedChange={(checked) =>
154
- addFormContent?.({
155
- privateBuild: checked,
156
- })
157
- }
158
- disabled={disabled}
159
- />
160
- </div>
161
-
162
272
  <div className="flex items-center justify-between">
163
273
  <div className="space-y-0.5">
164
274
  <Label className="text-sm font-medium">Enable PR Preview</Label>
@@ -167,10 +277,10 @@ export const GitHubRepositorySelector = ({
167
277
  </p>
168
278
  </div>
169
279
  <Switch
170
- checked={formData?.enablePrPreview}
280
+ checked={formData?.allowPrPreviews}
171
281
  onCheckedChange={(checked) =>
172
282
  addFormContent?.({
173
- enablePrPreview: checked,
283
+ allowPrPreviews: checked,
174
284
  })
175
285
  }
176
286
  disabled={disabled}
@@ -48,7 +48,9 @@ interface EditPackageDetailsDialogProps {
48
48
  newWebsite: string,
49
49
  newLicense: string | null,
50
50
  newDefaultView: string,
51
+ newAllowPrPreviews?: boolean,
51
52
  ) => void
53
+ currentAllowPrPreviews?: boolean
52
54
  }
53
55
 
54
56
  export const EditPackageDetailsDialog = ({
@@ -64,6 +66,7 @@ export const EditPackageDetailsDialog = ({
64
66
  unscopedPackageName,
65
67
  packageReleaseId,
66
68
  packageAuthor,
69
+ currentAllowPrPreviews,
67
70
  onUpdate,
68
71
  }: EditPackageDetailsDialogProps) => {
69
72
  const axios = useAxios()
@@ -85,6 +88,7 @@ export const EditPackageDetailsDialog = ({
85
88
  initialUnscopedPackageName: unscopedPackageName,
86
89
  isDialogOpen: open,
87
90
  initialVisibility: isPrivate ? "private" : "public",
91
+ initialAllowPrPreviews: currentAllowPrPreviews,
88
92
  })
89
93
 
90
94
  const [showConfirmDelete, setShowConfirmDelete] = useState(false)
@@ -117,6 +121,7 @@ export const EditPackageDetailsDialog = ({
117
121
  website: formData.website.trim(),
118
122
  is_private: formData.visibility == "private",
119
123
  default_view: formData.defaultView,
124
+ allow_pr_previews: formData.allowPrPreviews,
120
125
  github_repo_full_name:
121
126
  formData.githubRepoFullName === "unlink//repo"
122
127
  ? null
@@ -18,6 +18,7 @@ interface PackageDetailsForm {
18
18
  defaultView: string
19
19
  githubRepoFullName: string | null
20
20
  unscopedPackageName: string
21
+ allowPrPreviews?: boolean
21
22
  }
22
23
 
23
24
  interface UsePackageDetailsFormProps {
@@ -29,6 +30,7 @@ interface UsePackageDetailsFormProps {
29
30
  initialUnscopedPackageName: string
30
31
  initialGithubRepoFullName: string | null
31
32
  isDialogOpen: boolean
33
+ initialAllowPrPreviews?: boolean
32
34
  }
33
35
 
34
36
  export const usePackageDetailsForm = ({
@@ -40,6 +42,7 @@ export const usePackageDetailsForm = ({
40
42
  initialUnscopedPackageName,
41
43
  initialGithubRepoFullName,
42
44
  isDialogOpen,
45
+ initialAllowPrPreviews,
43
46
  }: UsePackageDetailsFormProps) => {
44
47
  const [formData, setFormData] = useState<PackageDetailsForm>({
45
48
  description: initialDescription,
@@ -62,6 +65,7 @@ export const usePackageDetailsForm = ({
62
65
  githubRepoFullName: initialGithubRepoFullName,
63
66
  defaultView: initialDefaultView,
64
67
  unscopedPackageName: initialUnscopedPackageName,
68
+ allowPrPreviews: initialAllowPrPreviews,
65
69
  })
66
70
  setWebsiteError(null)
67
71
  }
@@ -98,6 +102,11 @@ export const usePackageDetailsForm = ({
98
102
  [formData.defaultView, initialDefaultView],
99
103
  )
100
104
 
105
+ const hasAllowPrPreviewsChanged = useMemo(
106
+ () => formData.allowPrPreviews !== initialAllowPrPreviews,
107
+ [formData.allowPrPreviews, initialAllowPrPreviews],
108
+ )
109
+
101
110
  const hasChanges = useMemo(
102
111
  () =>
103
112
  formData.description !== initialDescription ||
@@ -106,7 +115,8 @@ export const usePackageDetailsForm = ({
106
115
  formData.visibility !== initialVisibility ||
107
116
  formData.defaultView !== initialDefaultView ||
108
117
  formData.githubRepoFullName !== initialGithubRepoFullName ||
109
- formData.unscopedPackageName !== initialUnscopedPackageName,
118
+ formData.unscopedPackageName !== initialUnscopedPackageName ||
119
+ formData.allowPrPreviews !== initialAllowPrPreviews,
110
120
  [
111
121
  formData,
112
122
  initialDescription,
@@ -116,6 +126,7 @@ export const usePackageDetailsForm = ({
116
126
  initialDefaultView,
117
127
  initialGithubRepoFullName,
118
128
  initialUnscopedPackageName,
129
+ initialAllowPrPreviews,
119
130
  ],
120
131
  )
121
132
 
@@ -128,6 +139,7 @@ export const usePackageDetailsForm = ({
128
139
  hasLicenseChanged,
129
140
  hasVisibilityChanged,
130
141
  hasDefaultViewChanged,
142
+ hasAllowPrPreviewsChanged,
131
143
  hasChanges,
132
144
  isFormValid,
133
145
  }