@tscircuit/fake-snippets 0.0.91 → 0.0.93

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/index.d.ts CHANGED
@@ -1069,7 +1069,7 @@ declare const createDatabase: ({ seed }?: {
1069
1069
  }[] | null;
1070
1070
  datasheet_pdf_urls: string[] | null;
1071
1071
  }[];
1072
- }, "addOrder" | "getOrderById" | "getOrderFilesByOrderId" | "addOrderQuote" | "getOrderQuoteById" | "getJlcpcbOrderStatesByOrderId" | "getJlcpcbOrderStepRunsByJlcpcbOrderStateId" | "updateOrder" | "addJlcpcbOrderState" | "updateJlcpcbOrderState" | "addOrderFile" | "getOrderFileById" | "addAccount" | "addAccountPackage" | "getAccountPackageById" | "updateAccountPackage" | "deleteAccountPackage" | "addSnippet" | "getLatestSnippets" | "getTrendingSnippets" | "getPackagesByAuthor" | "getSnippetByAuthorAndName" | "updateSnippet" | "getSnippetById" | "searchSnippets" | "searchPackages" | "deleteSnippet" | "addSession" | "getSessions" | "createLoginPage" | "getLoginPage" | "updateLoginPage" | "getAccount" | "updateAccount" | "createSession" | "addStar" | "removeStar" | "hasStarred" | "addPackage" | "updatePackage" | "getPackageById" | "getPackageReleaseById" | "addPackageRelease" | "updatePackageRelease" | "deletePackageFile" | "addPackageFile" | "updatePackageFile" | "getStarCount" | "getPackageFilesByReleaseId" | "updatePackageReleaseFsSha" | "addAiReview" | "updateAiReview" | "getAiReviewById" | "listAiReviews" | "addDatasheet" | "getDatasheetById" | "updateDatasheet"> & {
1072
+ }, "addOrder" | "getOrderById" | "getOrderFilesByOrderId" | "addOrderQuote" | "getOrderQuoteById" | "getJlcpcbOrderStatesByOrderId" | "getJlcpcbOrderStepRunsByJlcpcbOrderStateId" | "updateOrder" | "addJlcpcbOrderState" | "updateJlcpcbOrderState" | "addOrderFile" | "getOrderFileById" | "addAccount" | "addAccountPackage" | "getAccountPackageById" | "updateAccountPackage" | "deleteAccountPackage" | "addSnippet" | "getLatestSnippets" | "getTrendingSnippets" | "getPackagesByAuthor" | "getSnippetByAuthorAndName" | "updateSnippet" | "getSnippetById" | "searchSnippets" | "searchPackages" | "deleteSnippet" | "addSession" | "getSessions" | "createLoginPage" | "getLoginPage" | "updateLoginPage" | "getAccount" | "updateAccount" | "createSession" | "addStar" | "removeStar" | "hasStarred" | "addPackage" | "updatePackage" | "getPackageById" | "getPackageReleaseById" | "addPackageRelease" | "updatePackageRelease" | "deletePackageFile" | "addPackageFile" | "updatePackageFile" | "getStarCount" | "getPackageFilesByReleaseId" | "updatePackageReleaseFsSha" | "addAiReview" | "updateAiReview" | "getAiReviewById" | "listAiReviews" | "addDatasheet" | "getDatasheetById" | "listDatasheets" | "updateDatasheet"> & {
1073
1073
  addOrder: (order: Omit<Order, "order_id">) => Order;
1074
1074
  getOrderById: (orderId: string) => Order | undefined;
1075
1075
  getOrderFilesByOrderId: (orderId: string) => OrderFile[];
@@ -1155,6 +1155,10 @@ declare const createDatabase: ({ seed }?: {
1155
1155
  chip_name: string;
1156
1156
  }) => Datasheet;
1157
1157
  getDatasheetById: (datasheetId: string) => Datasheet | undefined;
1158
+ listDatasheets: ({ chip_name, is_popular, }?: {
1159
+ chip_name?: string;
1160
+ is_popular?: boolean;
1161
+ }) => Datasheet[];
1158
1162
  updateDatasheet: (datasheetId: string, updates: Partial<Datasheet>) => Datasheet | undefined;
1159
1163
  }> & Omit<{
1160
1164
  idCounter: number;
@@ -1416,7 +1420,7 @@ declare const createDatabase: ({ seed }?: {
1416
1420
  }[] | null;
1417
1421
  datasheet_pdf_urls: string[] | null;
1418
1422
  }[];
1419
- }, "addOrder" | "getOrderById" | "getOrderFilesByOrderId" | "addOrderQuote" | "getOrderQuoteById" | "getJlcpcbOrderStatesByOrderId" | "getJlcpcbOrderStepRunsByJlcpcbOrderStateId" | "updateOrder" | "addJlcpcbOrderState" | "updateJlcpcbOrderState" | "addOrderFile" | "getOrderFileById" | "addAccount" | "addAccountPackage" | "getAccountPackageById" | "updateAccountPackage" | "deleteAccountPackage" | "addSnippet" | "getLatestSnippets" | "getTrendingSnippets" | "getPackagesByAuthor" | "getSnippetByAuthorAndName" | "updateSnippet" | "getSnippetById" | "searchSnippets" | "searchPackages" | "deleteSnippet" | "addSession" | "getSessions" | "createLoginPage" | "getLoginPage" | "updateLoginPage" | "getAccount" | "updateAccount" | "createSession" | "addStar" | "removeStar" | "hasStarred" | "addPackage" | "updatePackage" | "getPackageById" | "getPackageReleaseById" | "addPackageRelease" | "updatePackageRelease" | "deletePackageFile" | "addPackageFile" | "updatePackageFile" | "getStarCount" | "getPackageFilesByReleaseId" | "updatePackageReleaseFsSha" | "addAiReview" | "updateAiReview" | "getAiReviewById" | "listAiReviews" | "addDatasheet" | "getDatasheetById" | "updateDatasheet"> & {
1423
+ }, "addOrder" | "getOrderById" | "getOrderFilesByOrderId" | "addOrderQuote" | "getOrderQuoteById" | "getJlcpcbOrderStatesByOrderId" | "getJlcpcbOrderStepRunsByJlcpcbOrderStateId" | "updateOrder" | "addJlcpcbOrderState" | "updateJlcpcbOrderState" | "addOrderFile" | "getOrderFileById" | "addAccount" | "addAccountPackage" | "getAccountPackageById" | "updateAccountPackage" | "deleteAccountPackage" | "addSnippet" | "getLatestSnippets" | "getTrendingSnippets" | "getPackagesByAuthor" | "getSnippetByAuthorAndName" | "updateSnippet" | "getSnippetById" | "searchSnippets" | "searchPackages" | "deleteSnippet" | "addSession" | "getSessions" | "createLoginPage" | "getLoginPage" | "updateLoginPage" | "getAccount" | "updateAccount" | "createSession" | "addStar" | "removeStar" | "hasStarred" | "addPackage" | "updatePackage" | "getPackageById" | "getPackageReleaseById" | "addPackageRelease" | "updatePackageRelease" | "deletePackageFile" | "addPackageFile" | "updatePackageFile" | "getStarCount" | "getPackageFilesByReleaseId" | "updatePackageReleaseFsSha" | "addAiReview" | "updateAiReview" | "getAiReviewById" | "listAiReviews" | "addDatasheet" | "getDatasheetById" | "listDatasheets" | "updateDatasheet"> & {
1420
1424
  addOrder: (order: Omit<Order, "order_id">) => Order;
1421
1425
  getOrderById: (orderId: string) => Order | undefined;
1422
1426
  getOrderFilesByOrderId: (orderId: string) => OrderFile[];
@@ -1502,6 +1506,10 @@ declare const createDatabase: ({ seed }?: {
1502
1506
  chip_name: string;
1503
1507
  }) => Datasheet;
1504
1508
  getDatasheetById: (datasheetId: string) => Datasheet | undefined;
1509
+ listDatasheets: ({ chip_name, is_popular, }?: {
1510
+ chip_name?: string;
1511
+ is_popular?: boolean;
1512
+ }) => Datasheet[];
1505
1513
  updateDatasheet: (datasheetId: string, updates: Partial<Datasheet>) => Datasheet | undefined;
1506
1514
  };
1507
1515
  type DbClient = ReturnType<typeof createDatabase>;
package/dist/index.js CHANGED
@@ -387,7 +387,8 @@ var loadPackageWithDependencies = async (db, owner, name, loadedPackages = /* @_
387
387
  ...pkg,
388
388
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
389
389
  updated_at: (/* @__PURE__ */ new Date()).toISOString(),
390
- latest_package_release_id: release.package_release_id
390
+ latest_package_release_id: release.package_release_id,
391
+ star_count: Math.floor(Math.random() * 11)
391
392
  });
392
393
  db.addPackageRelease({
393
394
  ...release,
@@ -3111,7 +3112,7 @@ var initializer = combine(databaseSchema.parse({}), (set, get) => ({
3111
3112
  },
3112
3113
  addDatasheet: ({ chip_name }) => {
3113
3114
  const newDatasheet = datasheetSchema.parse({
3114
- datasheet_id: `datasheet_${Date.now()}`,
3115
+ datasheet_id: crypto.randomUUID(),
3115
3116
  chip_name,
3116
3117
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
3117
3118
  pin_information: null,
@@ -3126,6 +3127,21 @@ var initializer = combine(databaseSchema.parse({}), (set, get) => ({
3126
3127
  const state = get();
3127
3128
  return state.datasheets.find((d) => d.datasheet_id === datasheetId);
3128
3129
  },
3130
+ listDatasheets: ({
3131
+ chip_name,
3132
+ is_popular
3133
+ } = {}) => {
3134
+ const state = get();
3135
+ if (is_popular) {
3136
+ return state.datasheets;
3137
+ }
3138
+ if (chip_name) {
3139
+ return state.datasheets.filter(
3140
+ (d) => d.chip_name.toLowerCase() === chip_name.toLowerCase()
3141
+ );
3142
+ }
3143
+ return state.datasheets;
3144
+ },
3129
3145
  updateDatasheet: (datasheetId, updates) => {
3130
3146
  let updated;
3131
3147
  set((state) => {
@@ -118,6 +118,7 @@ const loadPackageWithDependencies = async (
118
118
  created_at: new Date().toISOString(),
119
119
  updated_at: new Date().toISOString(),
120
120
  latest_package_release_id: release.package_release_id,
121
+ star_count: Math.floor(Math.random() * 11),
121
122
  })
122
123
 
123
124
  db.addPackageRelease({
@@ -1382,7 +1382,7 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
1382
1382
  },
1383
1383
  addDatasheet: ({ chip_name }: { chip_name: string }): Datasheet => {
1384
1384
  const newDatasheet = datasheetSchema.parse({
1385
- datasheet_id: `datasheet_${Date.now()}`,
1385
+ datasheet_id: crypto.randomUUID(),
1386
1386
  chip_name,
1387
1387
  created_at: new Date().toISOString(),
1388
1388
  pin_information: null,
@@ -1397,6 +1397,23 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
1397
1397
  const state = get()
1398
1398
  return state.datasheets.find((d) => d.datasheet_id === datasheetId)
1399
1399
  },
1400
+ listDatasheets: ({
1401
+ chip_name,
1402
+ is_popular,
1403
+ }: { chip_name?: string; is_popular?: boolean } = {}): Datasheet[] => {
1404
+ const state = get()
1405
+ if (is_popular) {
1406
+ return state.datasheets
1407
+ }
1408
+
1409
+ if (chip_name) {
1410
+ return state.datasheets.filter(
1411
+ (d) => d.chip_name.toLowerCase() === chip_name.toLowerCase(),
1412
+ )
1413
+ }
1414
+
1415
+ return state.datasheets
1416
+ },
1400
1417
  updateDatasheet: (
1401
1418
  datasheetId: string,
1402
1419
  updates: Partial<Datasheet>,
@@ -118,7 +118,6 @@ export default withRouteSpec({
118
118
  prediction: z.string(),
119
119
  }),
120
120
  })(async (req, ctx) => {
121
- return ctx.json({ prediction: "mock" })
122
121
  const openai = getOpenAIClient()
123
122
  const { prefix, suffix, model, language } = req.jsonBody
124
123
 
@@ -0,0 +1,29 @@
1
+ import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
2
+ import { z } from "zod"
3
+
4
+ export default withRouteSpec({
5
+ methods: ["GET", "POST"],
6
+ auth: "none",
7
+ commonParams: z.object({
8
+ chip_name: z.string().optional(),
9
+ is_popular: z.boolean().optional(),
10
+ }),
11
+ jsonResponse: z.object({
12
+ datasheets: z.array(
13
+ z.object({
14
+ datasheet_id: z.string().uuid(),
15
+ chip_name: z.string(),
16
+ }),
17
+ ),
18
+ }),
19
+ })(async (req, ctx) => {
20
+ const { chip_name, is_popular } = req.commonParams
21
+ const datasheets = ctx.db
22
+ .listDatasheets({ chip_name, is_popular })
23
+ .map((ds) => ({
24
+ datasheet_id: ds.datasheet_id,
25
+ chip_name: ds.chip_name,
26
+ }))
27
+
28
+ return ctx.json({ datasheets })
29
+ })
@@ -24,7 +24,7 @@ export default withRouteSpec({
24
24
  })
25
25
 
26
26
  // Filter out packages with no stars and sort by star count
27
- const trendingPackages = packagesWithStars
27
+ const trendingPackages = ctx.db.packages
28
28
  .filter((p) => p.star_count > 0)
29
29
  .sort((a, b) => b.star_count - a.star_count)
30
30
  .slice(0, 50)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.91",
3
+ "version": "0.0.93",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -282,7 +282,7 @@ const CmdKMenu = () => {
282
282
  switch (type) {
283
283
  case "package":
284
284
  case "recent":
285
- window.location.href = `/editor?package_id=${item.package_id}`
285
+ window.location.href = `/${item.owner_github_username}/${item.unscoped_name}`
286
286
  setOpen(false)
287
287
  break
288
288
  case "blank":
@@ -18,6 +18,7 @@ export const LogContent = ({
18
18
  logs: Array<{
19
19
  type?: "info" | "success" | "error"
20
20
  msg?: string
21
+ message?: string
21
22
  timestamp?: string
22
23
  }>
23
24
  error?: ErrorObject | string | null
@@ -25,7 +26,8 @@ export const LogContent = ({
25
26
  return (
26
27
  <div className="font-mono text-xs space-y-1 min-w-0">
27
28
  {logs.map((log, i) => {
28
- if (!log.msg) return null
29
+ const text = log.msg ?? log.message
30
+ if (!text) return null
29
31
 
30
32
  return (
31
33
  <div
@@ -44,7 +46,7 @@ export const LogContent = ({
44
46
  </span>
45
47
  )}
46
48
  {log.timestamp && " "}
47
- <span className="break-all">{log.msg}</span>
49
+ <span className="break-all">{text}</span>
48
50
  </div>
49
51
  )
50
52
  })}
@@ -99,6 +99,8 @@ export const PackageCard: React.FC<PackageCardProps> = ({
99
99
  })
100
100
  }
101
101
 
102
+ const availableImages = ["pcb", "schematic", "assembly", "3d"]
103
+
102
104
  const cardContent = (
103
105
  <div
104
106
  className={`border p-4 rounded-md hover:shadow-md transition-shadow flex flex-col gap-4 ${className}`}
@@ -108,8 +110,8 @@ export const PackageCard: React.FC<PackageCardProps> = ({
108
110
  className={`${imageSize} flex-shrink-0 rounded-md overflow-hidden`}
109
111
  >
110
112
  <ImageWithFallback
111
- src={`${baseUrl}/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.svg?fs_sha=${pkg.latest_package_release_fs_sha}`}
112
- alt={`${pkg.unscoped_name} PCB image`}
113
+ src={`${baseUrl}/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/${availableImages.includes(pkg.default_view || "") ? pkg.default_view : "3d"}.png?fs_sha=${pkg.latest_package_release_fs_sha}`}
114
+ alt={`${pkg.unscoped_name} ${availableImages.includes(pkg.default_view || "") ? pkg.default_view : "3D"} view`}
113
115
  className={`object-cover h-full w-full ${imageTransform}`}
114
116
  />
115
117
  </div>
@@ -224,34 +224,33 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
224
224
  pkgFilesLoaded={!isLoading}
225
225
  />
226
226
  </div>
227
- {state.showPreview && (
228
- <div
229
- className={cn(
230
- "flex p-0 flex-col min-h-[640px]",
231
- state.fullScreen
232
- ? "fixed inset-0 z-50 bg-white p-4 overflow-hidden"
233
- : "w-full md:w-1/2",
234
- )}
235
- >
236
- <SuspenseRunFrame
237
- showRunButton
238
- forceLatestEvalVersion
239
- onRenderStarted={() =>
240
- setState((prev) => ({ ...prev, lastRunCode: currentFileCode }))
241
- }
242
- onRenderFinished={({ circuitJson }) => {
243
- setState((prev) => ({ ...prev, circuitJson }))
244
- toastManualEditConflicts(circuitJson, toast)
245
- }}
246
- mainComponentPath={mainComponentPath}
247
- onEditEvent={(event) => {
248
- handleEditEvent(event)
249
- }}
250
- fsMap={fsMap ?? {}}
251
- projectUrl={projectUrl}
252
- />
253
- </div>
254
- )}
227
+ <div
228
+ className={cn(
229
+ "flex p-0 flex-col min-h-[640px]",
230
+ state.fullScreen
231
+ ? "fixed inset-0 z-50 bg-white p-4 overflow-hidden"
232
+ : "w-full md:w-1/2",
233
+ !state.showPreview && "hidden",
234
+ )}
235
+ >
236
+ <SuspenseRunFrame
237
+ showRunButton
238
+ forceLatestEvalVersion
239
+ onRenderStarted={() =>
240
+ setState((prev) => ({ ...prev, lastRunCode: currentFileCode }))
241
+ }
242
+ onRenderFinished={({ circuitJson }) => {
243
+ setState((prev) => ({ ...prev, circuitJson }))
244
+ toastManualEditConflicts(circuitJson, toast)
245
+ }}
246
+ mainComponentPath={mainComponentPath}
247
+ onEditEvent={(event) => {
248
+ handleEditEvent(event)
249
+ }}
250
+ fsMap={fsMap ?? {}}
251
+ projectUrl={projectUrl}
252
+ />
253
+ </div>
255
254
  </div>
256
255
  <NewPackageSaveDialog initialIsPrivate={false} onSave={savePackage} />
257
256
  <DiscardChangesDialog onConfirm={handleDiscardChanges} />
@@ -76,25 +76,27 @@ export const CodeEditor = ({
76
76
  showImportAndFormatButtons?: boolean
77
77
  onFileContentChanged?: (path: string, content: string) => void
78
78
  currentFile: string | null
79
- onFileSelect: (path: string) => void
79
+ onFileSelect: (path: string, lineNumber?: number) => void
80
80
  }) => {
81
81
  const editorRef = useRef<HTMLDivElement>(null)
82
82
  const viewRef = useRef<EditorView | null>(null)
83
83
  const ataRef = useRef<ReturnType<typeof setupTypeAcquisition> | null>(null)
84
84
  const lastReceivedTsFileTimeRef = useRef<number>(0)
85
85
  const apiUrl = usePackagesBaseApiUrl()
86
- const codeCompletionApi = useCodeCompletionApi()
87
86
  const [cursorPosition, setCursorPosition] = useState<number | null>(null)
88
87
  const [code, setCode] = useState(files[0]?.content || "")
89
88
  const [fontSize, setFontSize] = useState(14)
90
89
  const [showQuickOpen, setShowQuickOpen] = useState(false)
91
90
  const [showGlobalFindReplace, setShowGlobalFindReplace] = useState(false)
91
+ const [highlightedLine, setHighlightedLine] = useState<number | null>(null)
92
+ const highlightTimeoutRef = useRef<number | null>(null)
92
93
 
93
94
  const { highlighter } = useShikiHighlighter()
94
95
 
95
96
  // Get URL search params for file_path
96
97
  const urlParams = new URLSearchParams(window.location.search)
97
98
  const filePathFromUrl = urlParams.get("file_path")
99
+ const lineNumberFromUrl = urlParams.get("line")
98
100
  const [aiAutocompleteEnabled, setAiAutocompleteEnabled] = useState(false)
99
101
 
100
102
  const entryPointFileName = useMemo(() => {
@@ -109,10 +111,13 @@ export const CodeEditor = ({
109
111
 
110
112
  const targetFile = findTargetFile(files, filePathFromUrl)
111
113
  if (targetFile) {
112
- handleFileChange(targetFile.path)
114
+ const lineNumber = lineNumberFromUrl
115
+ ? parseInt(lineNumberFromUrl, 10)
116
+ : undefined
117
+ handleFileChange(targetFile.path, lineNumber)
113
118
  setCode(targetFile.content)
114
119
  }
115
- }, [filePathFromUrl, pkgFilesLoaded])
120
+ }, [filePathFromUrl, lineNumberFromUrl, pkgFilesLoaded])
116
121
 
117
122
  const fileMap = useMemo(() => {
118
123
  const map: Record<string, string> = {}
@@ -309,6 +314,14 @@ export const CodeEditor = ({
309
314
  ".cm-content": {
310
315
  fontSize: `${fontSize}px`,
311
316
  },
317
+ ".cm-line-highlight": {
318
+ backgroundColor: "#dbeafe !important",
319
+ animation: "lineHighlightFade 3s ease-in-out forwards",
320
+ },
321
+ "@keyframes lineHighlightFade": {
322
+ "0%": { backgroundColor: "#93c5fd" },
323
+ "100%": { backgroundColor: "transparent" },
324
+ },
312
325
  }),
313
326
  EditorView.domEventHandlers({
314
327
  wheel: (event) => {
@@ -325,6 +338,21 @@ export const CodeEditor = ({
325
338
  return false
326
339
  },
327
340
  }),
341
+ EditorView.decorations.of((view) => {
342
+ const decorations = []
343
+ if (highlightedLine) {
344
+ const doc = view.state.doc
345
+ if (highlightedLine >= 1 && highlightedLine <= doc.lines) {
346
+ const line = doc.line(highlightedLine)
347
+ decorations.push(
348
+ Decoration.line({
349
+ class: "cm-line-highlight",
350
+ }).range(line.from),
351
+ )
352
+ }
353
+ }
354
+ return Decoration.set(decorations)
355
+ }),
328
356
  ]
329
357
  if (aiAutocompleteEnabled) {
330
358
  baseExtensions.push(
@@ -546,6 +574,11 @@ export const CodeEditor = ({
546
574
 
547
575
  return () => {
548
576
  view.destroy()
577
+ // Clean up any pending highlight timeout
578
+ if (highlightTimeoutRef.current) {
579
+ window.clearTimeout(highlightTimeoutRef.current)
580
+ highlightTimeoutRef.current = null
581
+ }
549
582
  }
550
583
  }, [
551
584
  !isStreaming,
@@ -555,6 +588,7 @@ export const CodeEditor = ({
555
588
  isSaving,
556
589
  fontSize,
557
590
  aiAutocompleteEnabled,
591
+ highlightedLine,
558
592
  ])
559
593
 
560
594
  const updateCurrentEditorContent = (newContent: string) => {
@@ -574,6 +608,35 @@ export const CodeEditor = ({
574
608
  }
575
609
  }
576
610
 
611
+ const navigateToLine = (lineNumber: number) => {
612
+ if (!viewRef.current) return
613
+
614
+ const view = viewRef.current
615
+ const doc = view.state.doc
616
+
617
+ if (lineNumber < 1 || lineNumber > doc.lines) return
618
+
619
+ if (highlightTimeoutRef.current) {
620
+ window.clearTimeout(highlightTimeoutRef.current)
621
+ highlightTimeoutRef.current = null
622
+ }
623
+
624
+ const line = doc.line(lineNumber)
625
+ const pos = line.from
626
+
627
+ view.dispatch({
628
+ selection: { anchor: pos, head: pos },
629
+ effects: EditorView.scrollIntoView(pos, { y: "center" }),
630
+ })
631
+
632
+ setHighlightedLine(lineNumber)
633
+
634
+ highlightTimeoutRef.current = window.setTimeout(() => {
635
+ setHighlightedLine(null)
636
+ highlightTimeoutRef.current = null
637
+ }, 3000)
638
+ }
639
+
577
640
  const updateEditorToMatchCurrentFile = () => {
578
641
  const currentContent = fileMap[currentFile || ""] || ""
579
642
  updateCurrentEditorContent(currentContent)
@@ -590,14 +653,26 @@ export const CodeEditor = ({
590
653
  }
591
654
  }, [codeImports])
592
655
 
593
- const handleFileChange = (path: string) => {
594
- onFileSelect(path)
656
+ const handleFileChange = (path: string, lineNumber?: number) => {
657
+ onFileSelect(path, lineNumber)
595
658
  try {
596
- // Set url query to file path
659
+ // Set url query to file path and line number
597
660
  const urlParams = new URLSearchParams(window.location.search)
598
661
  urlParams.set("file_path", path)
662
+ if (lineNumber) {
663
+ urlParams.set("line", lineNumber.toString())
664
+ } else {
665
+ urlParams.delete("line")
666
+ }
599
667
  window.history.replaceState(null, "", `?${urlParams.toString()}`)
600
668
  } catch {}
669
+
670
+ // Navigate to line after a short delay to ensure editor is ready
671
+ if (lineNumber) {
672
+ setTimeout(() => {
673
+ navigateToLine(lineNumber)
674
+ }, 100)
675
+ }
601
676
  }
602
677
 
603
678
  const updateFileContent = (path: FileName | null, newContent: string) => {
@@ -656,7 +731,7 @@ export const CodeEditor = ({
656
731
  fileSidebarState={
657
732
  [sidebarOpen, setSidebarOpen] as ReturnType<typeof useState<boolean>>
658
733
  }
659
- onFileSelect={handleFileChange}
734
+ onFileSelect={(path) => handleFileChange(path)}
660
735
  handleCreateFile={handleCreateFile}
661
736
  handleDeleteFile={handleDeleteFile}
662
737
  />
@@ -694,7 +769,7 @@ export const CodeEditor = ({
694
769
  <QuickOpen
695
770
  files={files.filter((f) => !isHiddenFile(f.path))}
696
771
  currentFile={currentFile}
697
- onFileSelect={handleFileChange}
772
+ onFileSelect={(path) => handleFileChange(path)}
698
773
  onClose={() => setShowQuickOpen(false)}
699
774
  />
700
775
  )}
@@ -371,13 +371,6 @@ export default function EditorNav({
371
371
  </Button>
372
372
  </DropdownMenuTrigger>
373
373
  <DropdownMenuContent>
374
- <DropdownMenuItem
375
- className="text-xs"
376
- onClick={() => openupdateDescriptionDialog()}
377
- >
378
- <FilePenLine className="mr-2 h-3 w-3" />
379
- Edit Description
380
- </DropdownMenuItem>
381
374
  <DropdownMenuItem
382
375
  className="text-xs"
383
376
  onClick={() => openViewTsFilesDialog()}
@@ -387,6 +380,13 @@ export default function EditorNav({
387
380
  </DropdownMenuItem>
388
381
  {session?.github_username === pkg.owner_github_username && (
389
382
  <>
383
+ <DropdownMenuItem
384
+ className="text-xs"
385
+ onClick={() => openupdateDescriptionDialog()}
386
+ >
387
+ <FilePenLine className="mr-2 h-3 w-3" />
388
+ Edit Description
389
+ </DropdownMenuItem>
390
390
  <DropdownMenuSub>
391
391
  <DropdownMenuSubTrigger
392
392
  className="text-xs"
@@ -436,15 +436,15 @@ export default function EditorNav({
436
436
  </DropdownMenuItem>
437
437
  </DropdownMenuSubContent>
438
438
  </DropdownMenuSub>
439
- <DropdownMenuItem
440
- className="text-xs text-red-600"
441
- onClick={() => openDeleteDialog()}
442
- >
443
- <Trash2 className="mr-2 h-3 w-3" />
444
- Delete Package
445
- </DropdownMenuItem>
446
439
  </>
447
440
  )}
441
+ <DropdownMenuItem
442
+ className="text-xs text-red-600"
443
+ onClick={() => openDeleteDialog()}
444
+ >
445
+ <Trash2 className="mr-2 h-3 w-3" />
446
+ Delete Package
447
+ </DropdownMenuItem>
448
448
  <DropdownMenuItem className="text-xs text-gray-500" disabled>
449
449
  @tscircuit/core@{tscircuitCorePkg.version}
450
450
  </DropdownMenuItem>
@@ -44,7 +44,7 @@ interface FileMatch {
44
44
  interface GlobalFindReplaceProps {
45
45
  files: PackageFile[]
46
46
  currentFile: string | null
47
- onFileSelect: (path: string) => void
47
+ onFileSelect: (path: string, lineNumber?: number) => void
48
48
  onFileContentChanged?: (path: string, content: string) => void
49
49
  onClose: () => void
50
50
  }
@@ -217,7 +217,7 @@ const GlobalFindReplace = ({
217
217
 
218
218
  const goToMatch = useCallback(
219
219
  (fileMatch: FileMatch, match: Match) => {
220
- onFileSelect(fileMatch.file.path)
220
+ onFileSelect(fileMatch.file.path, match.line)
221
221
  onClose()
222
222
  },
223
223
  [onFileSelect, onClose],
@@ -71,7 +71,11 @@ export const DashboardPage = () => {
71
71
  const { data: latestPackages } = useQuery<Package[]>(
72
72
  "latestPackages",
73
73
  async () => {
74
- const response = await axios.get("/packages/list_latest")
74
+ const response = await axios.get("/packages/list_latest", {
75
+ params: {
76
+ limit: 10,
77
+ },
78
+ })
75
79
  return response.data.packages
76
80
  },
77
81
  {