@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.
- package/dist/bundle.js +420 -373
- package/dist/index.d.ts +15 -0
- package/dist/index.js +6 -2
- package/dist/schema.d.ts +24 -0
- package/dist/schema.js +6 -2
- package/fake-snippets-api/lib/db/schema.ts +5 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +2 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package.ts +2 -0
- package/fake-snippets-api/routes/api/github/repos/refresh.ts +44 -0
- package/fake-snippets-api/routes/api/packages/update.ts +3 -0
- package/package.json +1 -1
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -0
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +1 -0
- package/src/components/dialogs/GitHubRepositorySelector.tsx +189 -79
- package/src/components/dialogs/edit-package-details-dialog.tsx +5 -0
- package/src/hooks/use-package-details-form.ts +13 -1
|
@@ -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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "@/components/ui/
|
|
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?: (
|
|
23
|
-
|
|
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 {
|
|
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
|
|
58
|
-
|
|
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?.(
|
|
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
|
-
<
|
|
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
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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?.
|
|
280
|
+
checked={formData?.allowPrPreviews}
|
|
171
281
|
onCheckedChange={(checked) =>
|
|
172
282
|
addFormContent?.({
|
|
173
|
-
|
|
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
|
}
|