@tscircuit/fake-snippets 0.0.101 → 0.0.102
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/api/generated-index.js +23 -1
- package/bun.lock +2 -2
- package/dist/bundle.js +530 -367
- package/dist/index.d.ts +29 -2
- package/dist/index.js +18 -1
- package/dist/schema.d.ts +94 -1
- package/dist/schema.js +17 -1
- package/fake-snippets-api/lib/db/db-client.ts +6 -1
- package/fake-snippets-api/lib/db/schema.ts +15 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package.ts +2 -0
- package/fake-snippets-api/routes/api/github/installations/create_new_installation_redirect.ts +75 -0
- package/fake-snippets-api/routes/api/github/repos/list_available.ts +91 -0
- package/fake-snippets-api/routes/api/packages/update.ts +4 -0
- package/package.json +2 -2
- package/src/App.tsx +10 -1
- package/src/components/CreateReleaseDialog.tsx +124 -0
- package/src/components/FileSidebar.tsx +128 -23
- package/src/components/PackageBuildsPage/package-build-header.tsx +9 -1
- package/src/components/PageSearchComponent.tsx +2 -2
- package/src/components/SearchComponent.tsx +2 -2
- package/src/components/SuspenseRunFrame.tsx +2 -2
- package/src/components/TrendingPackagesCarousel.tsx +2 -2
- 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 +123 -0
- package/src/components/dialogs/create-use-dialog.tsx +8 -2
- package/src/components/dialogs/edit-package-details-dialog.tsx +22 -3
- package/src/components/dialogs/view-ts-files-dialog.tsx +178 -33
- package/src/components/package-port/CodeAndPreview.tsx +4 -1
- package/src/components/package-port/CodeEditor.tsx +42 -35
- package/src/components/package-port/CodeEditorHeader.tsx +6 -4
- package/src/components/package-port/EditorNav.tsx +94 -37
- package/src/components/preview/BuildsList.tsx +238 -0
- package/src/components/preview/ConnectedRepoDashboard.tsx +258 -0
- package/src/components/preview/ConnectedRepoOverview.tsx +454 -0
- package/src/components/preview/ConnectedRepoSettings.tsx +343 -0
- package/src/components/preview/ConnectedReposCards.tsx +191 -0
- package/src/components/preview/index.tsx +207 -0
- package/src/components/ui/tree-view.tsx +23 -6
- package/src/hooks/use-axios.ts +2 -2
- package/src/hooks/use-create-release-dialog.ts +160 -0
- package/src/hooks/use-package-details-form.ts +7 -0
- package/src/hooks/use-packages-base-api-url.ts +1 -1
- package/src/hooks/use-sign-in.ts +2 -2
- package/src/hooks/useFileManagement.ts +22 -2
- package/src/index.css +4 -0
- package/src/lib/utils/formatTimeAgo.ts +10 -0
- package/src/lib/utils/isValidFileName.ts +15 -3
- package/src/pages/dashboard.tsx +2 -2
- package/src/pages/dev-login.tsx +2 -2
- package/src/pages/latest.tsx +2 -2
- package/src/pages/preview-build.tsx +380 -0
- package/src/pages/search.tsx +2 -2
- package/src/pages/trending.tsx +2 -2
- package/src/pages/user-profile.tsx +32 -24
- package/src/pages/view-connected-repo.tsx +24 -0
|
@@ -5,14 +5,19 @@ export const createUseDialog = <DialogType extends React.ComponentType<any>>(
|
|
|
5
5
|
) => {
|
|
6
6
|
return () => {
|
|
7
7
|
const [open, setOpen] = useState(false)
|
|
8
|
+
const [dialogProps, setDialogProps] = useState<any>({})
|
|
8
9
|
|
|
9
10
|
return useMemo(
|
|
10
11
|
() => ({
|
|
11
|
-
openDialog: () => {
|
|
12
|
+
openDialog: (props?: any) => {
|
|
13
|
+
if (props) {
|
|
14
|
+
setDialogProps(props)
|
|
15
|
+
}
|
|
12
16
|
setOpen(true)
|
|
13
17
|
},
|
|
14
18
|
closeDialog: () => {
|
|
15
19
|
setOpen(false)
|
|
20
|
+
setDialogProps({})
|
|
16
21
|
},
|
|
17
22
|
Dialog: (
|
|
18
23
|
props: Omit<
|
|
@@ -22,13 +27,14 @@ export const createUseDialog = <DialogType extends React.ComponentType<any>>(
|
|
|
22
27
|
) => (
|
|
23
28
|
<DialogComponent
|
|
24
29
|
{...(props as any)}
|
|
30
|
+
{...dialogProps}
|
|
25
31
|
open={open}
|
|
26
32
|
onOpenChange={setOpen}
|
|
27
33
|
/>
|
|
28
34
|
),
|
|
29
35
|
open,
|
|
30
36
|
}),
|
|
31
|
-
[open],
|
|
37
|
+
[open, dialogProps],
|
|
32
38
|
)
|
|
33
39
|
}
|
|
34
40
|
}
|
|
@@ -27,6 +27,7 @@ import { createUseDialog } from "./create-use-dialog"
|
|
|
27
27
|
import { ChevronDown } from "lucide-react"
|
|
28
28
|
import { useLocation } from "wouter"
|
|
29
29
|
import { useDeletePackage } from "@/hooks/use-delete-package"
|
|
30
|
+
import { GitHubRepositorySelector } from "./GitHubRepositorySelector"
|
|
30
31
|
|
|
31
32
|
interface EditPackageDetailsDialogProps {
|
|
32
33
|
open: boolean
|
|
@@ -36,6 +37,7 @@ interface EditPackageDetailsDialogProps {
|
|
|
36
37
|
currentWebsite: string
|
|
37
38
|
currentLicense?: string | null
|
|
38
39
|
currentDefaultView?: string
|
|
40
|
+
currentGithubRepoFullName?: string | null
|
|
39
41
|
isPrivate?: boolean
|
|
40
42
|
packageName: string
|
|
41
43
|
unscopedPackageName: string
|
|
@@ -57,6 +59,7 @@ export const EditPackageDetailsDialog = ({
|
|
|
57
59
|
currentWebsite,
|
|
58
60
|
currentLicense,
|
|
59
61
|
currentDefaultView = "files",
|
|
62
|
+
currentGithubRepoFullName,
|
|
60
63
|
isPrivate = false,
|
|
61
64
|
unscopedPackageName,
|
|
62
65
|
packageReleaseId,
|
|
@@ -75,6 +78,7 @@ export const EditPackageDetailsDialog = ({
|
|
|
75
78
|
isFormValid,
|
|
76
79
|
} = usePackageDetailsForm({
|
|
77
80
|
initialDescription: currentDescription,
|
|
81
|
+
initialGithubRepoFullName: currentGithubRepoFullName ?? null,
|
|
78
82
|
initialWebsite: currentWebsite,
|
|
79
83
|
initialLicense: currentLicense || null,
|
|
80
84
|
initialDefaultView: currentDefaultView,
|
|
@@ -83,7 +87,6 @@ export const EditPackageDetailsDialog = ({
|
|
|
83
87
|
initialVisibility: isPrivate ? "private" : "public",
|
|
84
88
|
})
|
|
85
89
|
|
|
86
|
-
const [deleting, setDeleting] = useState(false)
|
|
87
90
|
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
|
88
91
|
const [dangerOpen, setDangerOpen] = useState(false)
|
|
89
92
|
const [, setLocation] = useLocation()
|
|
@@ -114,6 +117,7 @@ export const EditPackageDetailsDialog = ({
|
|
|
114
117
|
website: formData.website.trim(),
|
|
115
118
|
is_private: formData.visibility == "private",
|
|
116
119
|
default_view: formData.defaultView,
|
|
120
|
+
github_repo_full_name: formData.githubRepoFullName,
|
|
117
121
|
...(formData.unscopedPackageName !== unscopedPackageName && {
|
|
118
122
|
name: formData.unscopedPackageName.trim(),
|
|
119
123
|
}),
|
|
@@ -371,6 +375,19 @@ export const EditPackageDetailsDialog = ({
|
|
|
371
375
|
</SelectContent>
|
|
372
376
|
</Select>
|
|
373
377
|
</div>
|
|
378
|
+
<div className="space-y-1">
|
|
379
|
+
<GitHubRepositorySelector
|
|
380
|
+
value={formData.githubRepoFullName || ""}
|
|
381
|
+
onValueChange={(value) =>
|
|
382
|
+
setFormData((prev) => ({
|
|
383
|
+
...prev,
|
|
384
|
+
githubRepoFullName: value,
|
|
385
|
+
}))
|
|
386
|
+
}
|
|
387
|
+
disabled={updatePackageDetailsMutation.isLoading}
|
|
388
|
+
open={open}
|
|
389
|
+
/>
|
|
390
|
+
</div>
|
|
374
391
|
</div>
|
|
375
392
|
|
|
376
393
|
<details
|
|
@@ -394,10 +411,12 @@ export const EditPackageDetailsDialog = ({
|
|
|
394
411
|
variant="destructive"
|
|
395
412
|
size="default"
|
|
396
413
|
onClick={() => setShowConfirmDelete(true)}
|
|
397
|
-
disabled={
|
|
414
|
+
disabled={deletePackageMutation.isLoading}
|
|
398
415
|
className="shrink-0 lg:w-[115px] w-[70px]"
|
|
399
416
|
>
|
|
400
|
-
{
|
|
417
|
+
{deletePackageMutation.isLoading
|
|
418
|
+
? "Deleting..."
|
|
419
|
+
: "Delete"}
|
|
401
420
|
</Button>
|
|
402
421
|
</div>
|
|
403
422
|
</div>
|
|
@@ -21,13 +21,23 @@ import JSZip from "jszip"
|
|
|
21
21
|
import { saveAs } from "file-saver"
|
|
22
22
|
import { EditorView } from "codemirror"
|
|
23
23
|
import { EditorState } from "@codemirror/state"
|
|
24
|
+
import { autocompletion } from "@codemirror/autocomplete"
|
|
24
25
|
import { basicSetup } from "@/lib/codemirror/basic-setup"
|
|
25
26
|
import { javascript } from "@codemirror/lang-javascript"
|
|
26
27
|
import { json } from "@codemirror/lang-json"
|
|
28
|
+
import { tsAutocomplete, tsFacet, tsSync } from "@valtown/codemirror-ts"
|
|
29
|
+
import {
|
|
30
|
+
createSystem,
|
|
31
|
+
createVirtualTypeScriptEnvironment,
|
|
32
|
+
} from "@typescript/vfs"
|
|
33
|
+
import { loadDefaultLibMap } from "@/lib/ts-lib-cache"
|
|
34
|
+
import tsModule from "typescript"
|
|
27
35
|
|
|
28
36
|
interface ViewTsFilesDialogProps {
|
|
29
37
|
open: boolean
|
|
30
38
|
onOpenChange: (open: boolean) => void
|
|
39
|
+
initialFile?: string
|
|
40
|
+
initialLine?: number
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
interface FileNode {
|
|
@@ -41,6 +51,8 @@ interface FileNode {
|
|
|
41
51
|
export const ViewTsFilesDialog: React.FC<ViewTsFilesDialogProps> = ({
|
|
42
52
|
open,
|
|
43
53
|
onOpenChange,
|
|
54
|
+
initialFile,
|
|
55
|
+
initialLine,
|
|
44
56
|
}) => {
|
|
45
57
|
const [files, setFiles] = useState<Map<string, string>>(new Map())
|
|
46
58
|
const [selectedFile, setSelectedFile] = useState<string | null>(null)
|
|
@@ -48,8 +60,12 @@ export const ViewTsFilesDialog: React.FC<ViewTsFilesDialogProps> = ({
|
|
|
48
60
|
const [copiedFile, setCopiedFile] = useState<string | null>(null)
|
|
49
61
|
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set())
|
|
50
62
|
const [sidebarOpen, setSidebarOpen] = useState(true)
|
|
63
|
+
const [targetLine, setTargetLine] = useState<number | null>(null)
|
|
51
64
|
const editorRef = useRef<HTMLDivElement>(null)
|
|
52
65
|
const viewRef = useRef<EditorView | null>(null)
|
|
66
|
+
const [tsEnv, setTsEnv] = useState<ReturnType<
|
|
67
|
+
typeof createVirtualTypeScriptEnvironment
|
|
68
|
+
> | null>(null)
|
|
53
69
|
|
|
54
70
|
const fileTree = useMemo(() => {
|
|
55
71
|
const tree: FileNode[] = []
|
|
@@ -159,33 +175,86 @@ export const ViewTsFilesDialog: React.FC<ViewTsFilesDialogProps> = ({
|
|
|
159
175
|
viewRef.current.destroy()
|
|
160
176
|
}
|
|
161
177
|
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
"
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
"
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
178
|
+
const extensions = [
|
|
179
|
+
basicSetup,
|
|
180
|
+
isJson ? json() : javascript({ typescript: true, jsx: true }),
|
|
181
|
+
EditorState.readOnly.of(true),
|
|
182
|
+
EditorView.theme({
|
|
183
|
+
"&": {
|
|
184
|
+
height: "100%",
|
|
185
|
+
fontSize: "14px",
|
|
186
|
+
},
|
|
187
|
+
".cm-content": {
|
|
188
|
+
padding: "16px",
|
|
189
|
+
minHeight: "100%",
|
|
190
|
+
},
|
|
191
|
+
".cm-focused": {
|
|
192
|
+
outline: "none",
|
|
193
|
+
},
|
|
194
|
+
".cm-editor": {
|
|
195
|
+
height: "100%",
|
|
196
|
+
},
|
|
197
|
+
".cm-scroller": {
|
|
198
|
+
fontFamily:
|
|
199
|
+
"ui-monospace, SFMono-Regular, 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace",
|
|
200
|
+
},
|
|
201
|
+
}),
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
if (
|
|
205
|
+
tsEnv &&
|
|
206
|
+
!isJson &&
|
|
207
|
+
(selectedFile.endsWith(".ts") || selectedFile.endsWith(".tsx"))
|
|
208
|
+
) {
|
|
209
|
+
extensions.push(
|
|
210
|
+
tsFacet.of({
|
|
211
|
+
env: tsEnv,
|
|
212
|
+
path: selectedFile.startsWith("/")
|
|
213
|
+
? selectedFile.slice(1)
|
|
214
|
+
: selectedFile,
|
|
215
|
+
}),
|
|
216
|
+
tsSync(),
|
|
217
|
+
autocompletion({ override: [tsAutocomplete()] }),
|
|
218
|
+
EditorView.domEventHandlers({
|
|
219
|
+
click: (event, view) => {
|
|
220
|
+
if (event.ctrlKey || event.metaKey) {
|
|
221
|
+
const pos = view.posAtCoords({
|
|
222
|
+
x: event.clientX,
|
|
223
|
+
y: event.clientY,
|
|
224
|
+
})
|
|
225
|
+
if (pos !== null) {
|
|
226
|
+
const path = selectedFile.startsWith("/")
|
|
227
|
+
? selectedFile.slice(1)
|
|
228
|
+
: selectedFile
|
|
229
|
+
const definitions =
|
|
230
|
+
tsEnv.languageService.getDefinitionAtPosition(path, pos)
|
|
231
|
+
if (definitions && definitions.length > 0) {
|
|
232
|
+
const definition = definitions[0]
|
|
233
|
+
const definitionFileName = definition.fileName
|
|
234
|
+
if (definitionFileName && files.has(definitionFileName)) {
|
|
235
|
+
const definitionContent =
|
|
236
|
+
files.get(definitionFileName) || ""
|
|
237
|
+
const lines = definitionContent
|
|
238
|
+
.substring(0, definition.textSpan.start)
|
|
239
|
+
.split("\n")
|
|
240
|
+
const lineNumber = lines.length
|
|
241
|
+
|
|
242
|
+
setSelectedFile(definitionFileName)
|
|
243
|
+
setTargetLine(lineNumber)
|
|
244
|
+
return true
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return false
|
|
186
250
|
},
|
|
187
251
|
}),
|
|
188
|
-
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const state = EditorState.create({
|
|
256
|
+
doc: content,
|
|
257
|
+
extensions,
|
|
189
258
|
})
|
|
190
259
|
|
|
191
260
|
viewRef.current = new EditorView({
|
|
@@ -193,25 +262,85 @@ export const ViewTsFilesDialog: React.FC<ViewTsFilesDialogProps> = ({
|
|
|
193
262
|
parent: editorRef.current,
|
|
194
263
|
})
|
|
195
264
|
|
|
265
|
+
if (targetLine && targetLine > 0) {
|
|
266
|
+
const scrollToLine = () => {
|
|
267
|
+
if (viewRef.current) {
|
|
268
|
+
const doc = viewRef.current.state.doc
|
|
269
|
+
if (targetLine <= doc.lines) {
|
|
270
|
+
const line = doc.line(targetLine)
|
|
271
|
+
|
|
272
|
+
const performScroll = () => {
|
|
273
|
+
if (viewRef.current) {
|
|
274
|
+
viewRef.current.dispatch({
|
|
275
|
+
selection: { anchor: line.from, head: line.to },
|
|
276
|
+
effects: EditorView.scrollIntoView(line.from, {
|
|
277
|
+
y: "center",
|
|
278
|
+
}),
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
setTimeout(() => {
|
|
282
|
+
if (viewRef.current) {
|
|
283
|
+
viewRef.current.dispatch({
|
|
284
|
+
effects: EditorView.scrollIntoView(line.from, {
|
|
285
|
+
y: "center",
|
|
286
|
+
}),
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
setTimeout(() => {
|
|
290
|
+
if (viewRef.current) {
|
|
291
|
+
viewRef.current.dispatch({
|
|
292
|
+
effects: EditorView.scrollIntoView(line.from, {
|
|
293
|
+
y: "center",
|
|
294
|
+
}),
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
}, 200)
|
|
298
|
+
}
|
|
299
|
+
}, 150)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
requestAnimationFrame(performScroll)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Extra delay when TypeScript environment is not ready or for large line numbers
|
|
309
|
+
// This handles cases when triggered from CodeEditor.tsx with TypeScript definitions
|
|
310
|
+
const isLargeLine = targetLine > 100
|
|
311
|
+
const needsExtraDelay = !tsEnv || isLargeLine
|
|
312
|
+
const initialDelay = needsExtraDelay ? 500 : 200
|
|
313
|
+
|
|
314
|
+
setTimeout(scrollToLine, initialDelay)
|
|
315
|
+
setTargetLine(null)
|
|
316
|
+
}
|
|
317
|
+
|
|
196
318
|
return () => {
|
|
197
319
|
if (viewRef.current) {
|
|
198
320
|
viewRef.current.destroy()
|
|
199
321
|
viewRef.current = null
|
|
200
322
|
}
|
|
201
323
|
}
|
|
202
|
-
}, [selectedFile, files])
|
|
324
|
+
}, [selectedFile, files, targetLine, tsEnv])
|
|
203
325
|
|
|
204
326
|
useEffect(() => {
|
|
205
327
|
if (open && window.__DEBUG_CODE_EDITOR_FS_MAP) {
|
|
206
328
|
setFiles(window.__DEBUG_CODE_EDITOR_FS_MAP)
|
|
207
329
|
|
|
208
330
|
if (window.__DEBUG_CODE_EDITOR_FS_MAP.size > 0) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
331
|
+
let fileToSelect: string
|
|
332
|
+
if (initialFile && window.__DEBUG_CODE_EDITOR_FS_MAP.has(initialFile)) {
|
|
333
|
+
fileToSelect = initialFile
|
|
334
|
+
if (initialLine) {
|
|
335
|
+
setTargetLine(initialLine)
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
fileToSelect = Array.from(window.__DEBUG_CODE_EDITOR_FS_MAP.keys())[0]
|
|
339
|
+
}
|
|
213
340
|
|
|
214
|
-
|
|
341
|
+
setSelectedFile(fileToSelect)
|
|
342
|
+
|
|
343
|
+
let normalizedPath = fileToSelect
|
|
215
344
|
if (normalizedPath.startsWith("/")) {
|
|
216
345
|
normalizedPath = normalizedPath.slice(1)
|
|
217
346
|
}
|
|
@@ -225,7 +354,23 @@ export const ViewTsFilesDialog: React.FC<ViewTsFilesDialogProps> = ({
|
|
|
225
354
|
setExpandedFolders(foldersToExpand)
|
|
226
355
|
}
|
|
227
356
|
}
|
|
228
|
-
}, [open])
|
|
357
|
+
}, [open, initialFile, initialLine])
|
|
358
|
+
|
|
359
|
+
useEffect(() => {
|
|
360
|
+
if (files.size > 0) {
|
|
361
|
+
const setupTsEnv = async () => {
|
|
362
|
+
try {
|
|
363
|
+
const libMap = await loadDefaultLibMap()
|
|
364
|
+
const system = createSystem(new Map([...libMap, ...files]))
|
|
365
|
+
const env = createVirtualTypeScriptEnvironment(system, [], tsModule)
|
|
366
|
+
setTsEnv(env)
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error("Failed to setup TypeScript environment:", error)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
setupTsEnv()
|
|
372
|
+
}
|
|
373
|
+
}, [files])
|
|
229
374
|
|
|
230
375
|
useEffect(() => {
|
|
231
376
|
const handleResize = () => {
|
|
@@ -367,7 +512,7 @@ export const ViewTsFilesDialog: React.FC<ViewTsFilesDialogProps> = ({
|
|
|
367
512
|
</Badge>
|
|
368
513
|
</div>
|
|
369
514
|
</div>
|
|
370
|
-
<div className="flex items-center gap-2">
|
|
515
|
+
<div className="flex items-center gap-2 mr-4">
|
|
371
516
|
<Button
|
|
372
517
|
variant="outline"
|
|
373
518
|
size="sm"
|
|
@@ -78,6 +78,7 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
|
|
|
78
78
|
localFiles,
|
|
79
79
|
initialFiles,
|
|
80
80
|
renameFile,
|
|
81
|
+
packageFilesMeta,
|
|
81
82
|
} = useFileManagement({
|
|
82
83
|
templateCode: templateFromUrl?.code,
|
|
83
84
|
currentPackage: pkg,
|
|
@@ -197,6 +198,8 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
|
|
|
197
198
|
setState((prev) => ({ ...prev, showPreview: !prev.showPreview }))
|
|
198
199
|
}
|
|
199
200
|
previewOpen={state.showPreview}
|
|
201
|
+
files={localFiles}
|
|
202
|
+
packageFilesMeta={packageFilesMeta}
|
|
200
203
|
/>
|
|
201
204
|
<div
|
|
202
205
|
className={`flex ${state.showPreview ? "flex-col md:flex-row" : ""}`}
|
|
@@ -231,7 +234,7 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
|
|
|
231
234
|
</div>
|
|
232
235
|
<div
|
|
233
236
|
className={cn(
|
|
234
|
-
"flex p-0 flex-col min-h-[640px]",
|
|
237
|
+
"flex p-0 flex-col min-h-[640px] overflow-y-hidden",
|
|
235
238
|
state.fullScreen
|
|
236
239
|
? "fixed inset-0 z-50 bg-white p-4 overflow-hidden"
|
|
237
240
|
: "w-full md:w-1/2",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useApiBaseUrl } from "@/hooks/use-packages-base-api-url"
|
|
2
2
|
import { useHotkeyCombo } from "@/hooks/use-hotkey"
|
|
3
3
|
import { basicSetup } from "@/lib/codemirror/basic-setup"
|
|
4
4
|
import {
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
} from "@/hooks/useFileManagement"
|
|
50
50
|
import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
|
|
51
51
|
import { inlineCopilot } from "codemirror-copilot"
|
|
52
|
-
import {
|
|
52
|
+
import { useViewTsFilesDialog } from "@/components/dialogs/view-ts-files-dialog"
|
|
53
53
|
|
|
54
54
|
const defaultImports = `
|
|
55
55
|
import React from "@types/react/jsx-runtime"
|
|
@@ -92,7 +92,7 @@ export const CodeEditor = ({
|
|
|
92
92
|
const viewRef = useRef<EditorView | null>(null)
|
|
93
93
|
const ataRef = useRef<ReturnType<typeof setupTypeAcquisition> | null>(null)
|
|
94
94
|
const lastReceivedTsFileTimeRef = useRef<number>(0)
|
|
95
|
-
const apiUrl =
|
|
95
|
+
const apiUrl = useApiBaseUrl()
|
|
96
96
|
const [cursorPosition, setCursorPosition] = useState<number | null>(null)
|
|
97
97
|
const [code, setCode] = useState(files[0]?.content || "")
|
|
98
98
|
const [fontSize, setFontSize] = useState(14)
|
|
@@ -108,6 +108,8 @@ export const CodeEditor = ({
|
|
|
108
108
|
const filePathFromUrl = urlParams.get("file_path")
|
|
109
109
|
const lineNumberFromUrl = urlParams.get("line")
|
|
110
110
|
const [aiAutocompleteEnabled, setAiAutocompleteEnabled] = useState(false)
|
|
111
|
+
const { Dialog: ViewTsFilesDialog, openDialog: openViewTsFilesDialog } =
|
|
112
|
+
useViewTsFilesDialog()
|
|
111
113
|
|
|
112
114
|
const entryPointFileName = useMemo(() => {
|
|
113
115
|
const entryPointFile = findTargetFile(files, null)
|
|
@@ -519,42 +521,47 @@ export const CodeEditor = ({
|
|
|
519
521
|
}
|
|
520
522
|
}
|
|
521
523
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (
|
|
529
|
-
const
|
|
530
|
-
const
|
|
531
|
-
if (
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
524
|
+
// TypeScript "Go to Definition" functionality
|
|
525
|
+
const facet = view.state.facet(tsFacet)
|
|
526
|
+
if (facet) {
|
|
527
|
+
const { env, path } = facet
|
|
528
|
+
const definitions =
|
|
529
|
+
env.languageService.getDefinitionAtPosition(path, pos)
|
|
530
|
+
if (definitions && definitions.length > 0) {
|
|
531
|
+
const definition = definitions[0]
|
|
532
|
+
const definitionFileName = definition.fileName
|
|
533
|
+
if (definitionFileName) {
|
|
534
|
+
const localFilePath = definitionFileName.startsWith("/")
|
|
535
|
+
? definitionFileName.replace("/", "")
|
|
536
|
+
: definitionFileName
|
|
537
|
+
if (fileMap[localFilePath]) {
|
|
538
|
+
const definitionContent = fileMap[localFilePath]
|
|
539
|
+
const lines = definitionContent
|
|
540
|
+
?.substring(0, definition.textSpan.start)
|
|
541
|
+
.split("\n")
|
|
542
|
+
const lineNumber = lines?.length
|
|
543
|
+
|
|
544
|
+
onFileSelect(localFilePath, lineNumber)
|
|
545
|
+
return true
|
|
546
|
+
} else {
|
|
547
|
+
const definitionContent =
|
|
548
|
+
env
|
|
549
|
+
.getSourceFile(definitionFileName)
|
|
550
|
+
?.getFullText() || ""
|
|
551
|
+
const lines = definitionContent
|
|
552
|
+
.substring(0, definition.textSpan.start)
|
|
553
|
+
.split("\n")
|
|
554
|
+
const lineNumber = lines.length
|
|
555
|
+
openViewTsFilesDialog({
|
|
556
|
+
initialFile: definitionFileName,
|
|
557
|
+
initialLine: lineNumber,
|
|
558
|
+
})
|
|
552
559
|
return true
|
|
553
560
|
}
|
|
554
|
-
return !!fileMap[targetPath]
|
|
555
561
|
}
|
|
556
562
|
}
|
|
557
563
|
}
|
|
564
|
+
|
|
558
565
|
return false
|
|
559
566
|
},
|
|
560
567
|
keydown: (event) => {
|
|
@@ -660,7 +667,6 @@ export const CodeEditor = ({
|
|
|
660
667
|
}, [
|
|
661
668
|
!isStreaming,
|
|
662
669
|
currentFile,
|
|
663
|
-
code !== "",
|
|
664
670
|
Boolean(highlighter),
|
|
665
671
|
isSaving,
|
|
666
672
|
fontSize,
|
|
@@ -867,6 +873,7 @@ export const CodeEditor = ({
|
|
|
867
873
|
onClose={() => setShowGlobalFindReplace(false)}
|
|
868
874
|
/>
|
|
869
875
|
)}
|
|
876
|
+
<ViewTsFilesDialog />
|
|
870
877
|
</div>
|
|
871
878
|
)
|
|
872
879
|
}
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
} from "@/components/ui/tooltip"
|
|
28
28
|
import { convertRawEasyToTsx, fetchEasyEDAComponent } from "easyeda/browser"
|
|
29
29
|
import { ComponentSearchResult } from "@tscircuit/runframe/runner"
|
|
30
|
-
import {
|
|
30
|
+
import { useApiBaseUrl } from "@/hooks/use-packages-base-api-url"
|
|
31
31
|
import { ICreateFileProps, ICreateFileResult } from "@/hooks/useFileManagement"
|
|
32
32
|
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
33
33
|
|
|
@@ -60,7 +60,7 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
|
|
|
60
60
|
useImportComponentDialog()
|
|
61
61
|
const { toast, toastLibrary } = useToast()
|
|
62
62
|
const [sidebarOpen, setSidebarOpen] = fileSidebarState
|
|
63
|
-
const API_BASE =
|
|
63
|
+
const API_BASE = useApiBaseUrl()
|
|
64
64
|
const [aiAutocompleteEnabled, setAiAutocompleteEnabled] = aiAutocompleteState
|
|
65
65
|
const session = useGlobalStore((s) => s.session)
|
|
66
66
|
|
|
@@ -184,11 +184,13 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
|
|
|
184
184
|
)
|
|
185
185
|
const tsxComponent = await convertRawEasyToTsx(jlcpcbComponent)
|
|
186
186
|
let componentName = component.name.replace(/ /g, "-")
|
|
187
|
-
|
|
187
|
+
let componentPath = `imports/${componentName}.tsx`
|
|
188
|
+
if (files[componentPath] || files[`./${componentPath}`]) {
|
|
188
189
|
componentName = `${componentName}-1`
|
|
190
|
+
componentPath = `imports/${componentName}.tsx`
|
|
189
191
|
}
|
|
190
192
|
const createFileResult = createFile({
|
|
191
|
-
newFileName:
|
|
193
|
+
newFileName: componentPath,
|
|
192
194
|
content: tsxComponent,
|
|
193
195
|
onError: (error) => {
|
|
194
196
|
throw error
|