@tscircuit/fake-snippets 0.0.89 → 0.0.90

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.
@@ -0,0 +1,134 @@
1
+ import "dotenv/config"
2
+ import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
3
+ import { z } from "zod"
4
+ import OpenAI from "openai"
5
+
6
+ // Lazy-loaded client instance
7
+ let openai: OpenAI | null = null
8
+ let cachedReadme: string | null = null
9
+
10
+ function getOpenAIClient() {
11
+ const apiKey = process.env.VITE_OPENROUTER_API_KEY
12
+ if (!apiKey) {
13
+ throw new Error("Missing Api Key in env")
14
+ }
15
+
16
+ if (!openai) {
17
+ openai = new OpenAI({
18
+ apiKey,
19
+ baseURL: "https://openrouter.ai/api/v1",
20
+ defaultHeaders: {
21
+ "HTTP-Referer": "https://tscircuit.com",
22
+ "X-Title": "TSCircuit Editor",
23
+ },
24
+ })
25
+ }
26
+
27
+ return openai
28
+ }
29
+
30
+ // Cache README
31
+ async function getCachedReadme(): Promise<string> {
32
+ if (cachedReadme !== null) return cachedReadme
33
+ const res = await fetch(
34
+ "https://raw.githubusercontent.com/tscircuit/props/main/README.md",
35
+ )
36
+ if (!res.ok) {
37
+ throw new Error(`Failed to fetch README: ${res.status}`)
38
+ }
39
+ cachedReadme = await res.text()
40
+ return cachedReadme
41
+ }
42
+
43
+ async function completion(
44
+ openai: OpenAI,
45
+ readmeContent: string,
46
+ prefix: string,
47
+ suffix: string,
48
+ model = "openai/gpt-4.1-mini",
49
+ language?: string,
50
+ ) {
51
+ const systemMessage = `You are an expert ${language ? language + " " : ""}programmer working in a TSX (TypeScript + React JSX) environment.
52
+
53
+ Below is the README.md for the available components. You MUST use this to determine which components and props are valid.
54
+ Only use components explicitly documented under Available Components in the README. Never invent or guess new components. If the user partially types a component that does not exist in the README, do NOT try to complete it.
55
+
56
+ ===== README.md START =====
57
+ ${readmeContent}
58
+ ===== README.md END =====
59
+
60
+ Special instruction for the <chip> component:
61
+ - Do NOT add chip as a prop (e.g., <chip chip="..."> is invalid).
62
+ - Always use this format:
63
+ <chip name="U<number>" footprint="<valid footprint>" pinLabels={{}} pcbX={0} pcbY={0} schX={0} schY={0} />
64
+ - Determine the next sequential name automatically: e.g. U1, U2, U3.
65
+ - Only use valid footprints and pinLabels from the README.
66
+ - Some components like <netlabel> do not have a 'name' prop — do not add it for those.
67
+
68
+ STRICT rules:
69
+ - If partial like "<capa", only append remaining "citor". Never repeat letters.
70
+ - If input is "<capacitor", add only props, never repeat tag.
71
+ - Always produce exactly one JSX component, starting with "<" if needed.
72
+ - If partial doesn’t match any valid component, output nothing.
73
+ - Never output two JSX elements. Always end with exactly one "/>".
74
+ - Never add duplicate closing "/>".
75
+ - Never output the component name as a prop.
76
+ - Never add whitespace before your completion.
77
+ - If the input is exactly "<", then start with the component name directly (like "resistor ... />") without adding another "<".
78
+ - So that the final result is "<resistor ... />", not "<<resistor ... />".
79
+ - Never produce a double "<".
80
+
81
+ Examples:
82
+ - Input: "<FILL_ME>"
83
+ Output: <resistor name="R1" footprint="0603" pcbX={5} pcbY={7} schX={1} schY={2} resistance={1000} />
84
+ - Input: "<ca<FILL_ME>"
85
+ Output: pacitor name="C1" footprint="0805" pcbX={10} pcbY={15} schX={3} schY={4} />
86
+ - Input: "<chip<FILL_ME>"
87
+ Output: name="U1" footprint="SOIC-8" pinLabels={{}} pcbX={0} pcbY={0} schX={0} schY={0} />
88
+ - Input: "<netl<FILL_ME>"
89
+ Output: abel name="N1" />
90
+ - NEVER output: <capacitor capacitor ... /> or <netnet ... />
91
+ - Input: "<"
92
+ Output: resistor name="R1" footprint="0603" pcbX={5} pcbY={7} schX={1} schY={2} resistance={1000} />
93
+ - Input: "<ca"
94
+ Output: pacitor name="C1" footprint="0805" pcbX={10} pcbY={15} schX={3} schY={4} />
95
+ - Input: "<capacitor"
96
+ Output: capacitance="1000pF" footprint="0805" name="C1" pcbX={10} pcbY={15} schX={3} schY={4} />`
97
+
98
+ const chatCompletion = await openai.chat.completions.create({
99
+ messages: [
100
+ { role: "system", content: systemMessage },
101
+ { role: "user", content: `${prefix}<FILL_ME>${suffix}` },
102
+ ],
103
+ model,
104
+ })
105
+
106
+ return chatCompletion.choices[0].message?.content ?? ""
107
+ }
108
+ export default withRouteSpec({
109
+ methods: ["POST"],
110
+ auth: "session", // ✅ Require user to be signed in
111
+ jsonBody: z.object({
112
+ prefix: z.string(),
113
+ suffix: z.string(),
114
+ model: z.string().optional(),
115
+ language: z.string().optional(),
116
+ }),
117
+ jsonResponse: z.object({
118
+ prediction: z.string(),
119
+ }),
120
+ })(async (req, ctx) => {
121
+ const openai = getOpenAIClient()
122
+ const { prefix, suffix, model, language } = req.jsonBody
123
+
124
+ const readmeContent = await getCachedReadme()
125
+ const predictionResult = await completion(
126
+ openai,
127
+ readmeContent,
128
+ prefix,
129
+ suffix,
130
+ model,
131
+ language,
132
+ )
133
+ return ctx.json({ prediction: predictionResult })
134
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.89",
3
+ "version": "0.0.90",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -87,7 +87,7 @@
87
87
  "@tscircuit/pcb-viewer": "^1.11.194",
88
88
  "@tscircuit/prompt-benchmarks": "^0.0.28",
89
89
  "@tscircuit/props": "^0.0.246",
90
- "@tscircuit/runframe": "^0.0.653",
90
+ "@tscircuit/runframe": "^0.0.669",
91
91
  "@tscircuit/schematic-viewer": "^2.0.21",
92
92
  "@types/babel__standalone": "^7.1.7",
93
93
  "@types/bun": "^1.1.10",
@@ -103,7 +103,6 @@
103
103
  "@types/sharp": "^0.32.0",
104
104
  "@typescript/ata": "^0.9.7",
105
105
  "@typescript/vfs": "^1.6.0",
106
- "@valtown/codemirror-codeium": "^1.1.1",
107
106
  "@valtown/codemirror-ts": "^2.2.0",
108
107
  "@vercel/analytics": "^1.4.1",
109
108
  "@vitejs/plugin-react": "^4.3.1",
@@ -120,8 +119,10 @@
120
119
  "clsx": "^2.1.1",
121
120
  "cmdk": "^1.0.4",
122
121
  "codemirror": "^6.0.1",
122
+ "codemirror-copilot": "^0.0.7",
123
123
  "country-list": "^2.3.0",
124
124
  "date-fns": "^4.1.0",
125
+ "dotenv": "^16.5.0",
125
126
  "dsn-converter": "^0.0.60",
126
127
  "easyeda": "^0.0.195",
127
128
  "embla-carousel-react": "^8.3.0",
@@ -145,6 +146,7 @@
145
146
  "md5": "^2.3.0",
146
147
  "ms": "^2.1.3",
147
148
  "next-themes": "^0.3.0",
149
+ "openai": "^5.6.0",
148
150
  "postcss": "^8.4.47",
149
151
  "posthog-js": "^1.203.2",
150
152
  "prismjs": "^1.29.0",
package/src/App.tsx CHANGED
@@ -80,6 +80,9 @@ class ErrorBoundary extends React.Component<
80
80
  { children: React.ReactNode },
81
81
  { hasError: boolean; reloading: boolean }
82
82
  > {
83
+ private visibilityHandler?: () => void
84
+ private reloadTimeout?: number
85
+
83
86
  constructor(props: { children: React.ReactNode }) {
84
87
  super(props)
85
88
  this.state = { hasError: false, reloading: false }
@@ -99,12 +102,54 @@ class ErrorBoundary extends React.Component<
99
102
  ) {
100
103
  const loadedAt = window.__APP_LOADED_AT || Date.now()
101
104
  if (Date.now() - loadedAt >= 10_000) {
102
- this.setState({ reloading: true })
103
- window.location.reload()
105
+ this.performReload()
104
106
  }
105
107
  }
106
108
  }
107
109
 
110
+ componentDidUpdate(_prevProps: any, prevState: any) {
111
+ if (!prevState.hasError && this.state.hasError && !this.state.reloading) {
112
+ this.setupIdleReload()
113
+ }
114
+ }
115
+
116
+ componentWillUnmount() {
117
+ this.cleanup()
118
+ }
119
+
120
+ cleanup = () => {
121
+ if (this.visibilityHandler) {
122
+ document.removeEventListener("visibilitychange", this.visibilityHandler)
123
+ this.visibilityHandler = undefined
124
+ }
125
+ if (this.reloadTimeout) {
126
+ clearTimeout(this.reloadTimeout)
127
+ this.reloadTimeout = undefined
128
+ }
129
+ }
130
+
131
+ performReload = () => {
132
+ if (this.state.reloading) return // Prevent multiple reloads
133
+
134
+ this.cleanup() // Clean up listeners before reload
135
+ this.setState({ reloading: true })
136
+ this.reloadTimeout = window.setTimeout(() => {
137
+ window.location.reload()
138
+ }, 500)
139
+ }
140
+
141
+ setupIdleReload = () => {
142
+ this.cleanup() // Clean up any existing handlers
143
+
144
+ this.visibilityHandler = () => {
145
+ if (!document.hidden && this.state.hasError && !this.state.reloading) {
146
+ this.performReload()
147
+ }
148
+ }
149
+
150
+ document.addEventListener("visibilitychange", this.visibilityHandler)
151
+ }
152
+
108
153
  render() {
109
154
  if (this.state.reloading) {
110
155
  return <div>There was a problem loading this page. Reloading…</div>
@@ -15,7 +15,7 @@ import { useLocation } from "wouter"
15
15
  import { useGlobalStore } from "@/hooks/use-global-store"
16
16
  import { convertCircuitJsonToTscircuit } from "circuit-json-to-tscircuit"
17
17
  import { useCreatePackageMutation } from "@/hooks/use-create-package-mutation"
18
- import { generateRandomPackageName } from "./package-port/CodeAndPreview"
18
+ import { generateRandomPackageName } from "@/lib/utils/package-utils"
19
19
  import { useCreatePackageReleaseMutation } from "@/hooks/use-create-package-release-mutation"
20
20
  import { useCreatePackageFilesMutation } from "@/hooks/use-create-package-files-mutation"
21
21
 
@@ -1,13 +1,13 @@
1
1
  import { Button } from "@/components/ui/button"
2
2
  import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
3
3
  import { useRebuildPackageReleaseMutation } from "@/hooks/use-rebuild-package-release-mutation"
4
- import { Github, RefreshCw, RotateCcw } from "lucide-react"
4
+ import { Github, RefreshCw } from "lucide-react"
5
5
  import { useParams } from "wouter"
6
6
  import { DownloadButtonAndMenu } from "../DownloadButtonAndMenu"
7
7
 
8
8
  export function PackageBuildHeader() {
9
9
  const { author, packageName } = useParams()
10
- const { packageRelease, refetch, isFetching } = useCurrentPackageRelease({
10
+ const { packageRelease } = useCurrentPackageRelease({
11
11
  include_logs: true,
12
12
  })
13
13
  const { mutate: rebuildPackage, isLoading } =
@@ -59,16 +59,6 @@ export function PackageBuildHeader() {
59
59
  <RefreshCw className="w-3 h-3 sm:w-4 sm:h-4 mr-1 sm:mr-2" />
60
60
  {isLoading ? "Rebuilding..." : "Rebuild"}
61
61
  </Button>
62
- <Button
63
- variant="outline"
64
- size="icon"
65
- aria-label="Reload logs"
66
- className="border-gray-300 bg-white hover:bg-gray-50"
67
- onClick={() => refetch()}
68
- disabled={isFetching}
69
- >
70
- <RotateCcw className="w-3 h-3 sm:w-4 sm:h-4" />
71
- </Button>
72
62
  <DownloadButtonAndMenu unscopedName={packageName} author={author} />
73
63
  </div>
74
64
  </div>
@@ -15,6 +15,7 @@ import { applyEditEventsToManualEditsFile } from "@tscircuit/core"
15
15
  import { toastManualEditConflicts } from "@/lib/utils/toastManualEditConflicts"
16
16
  import { ManualEditEvent } from "@tscircuit/props"
17
17
  import { useFileManagement } from "@/hooks/useFileManagement"
18
+ import { DEFAULT_CODE } from "@/lib/utils/package-utils"
18
19
 
19
20
  interface Props {
20
21
  pkg?: Package
@@ -25,11 +26,6 @@ interface Props {
25
26
  projectUrl?: string
26
27
  }
27
28
 
28
- export interface PackageFile {
29
- path: string
30
- content: string
31
- }
32
-
33
29
  export interface CodeAndPreviewState {
34
30
  showPreview: boolean
35
31
  fullScreen: boolean
@@ -40,17 +36,6 @@ export interface CodeAndPreviewState {
40
36
  defaultComponentFile?: string
41
37
  }
42
38
 
43
- export const DEFAULT_CODE = `
44
- export default () => (
45
- <board width="10mm" height="10mm">
46
- {/* write your code here! */}
47
- </board>
48
- )
49
- `.trim()
50
-
51
- export const generateRandomPackageName = () =>
52
- `untitled-package-${Math.floor(Math.random() * 900) + 100}`
53
-
54
39
  export function CodeAndPreview({ pkg, projectUrl }: Props) {
55
40
  const { toast } = useToast()
56
41
  const urlParams = useUrlParams()
@@ -32,7 +32,7 @@ import CodeEditorHeader, {
32
32
  import { useCodeCompletionApi } from "@/hooks/use-code-completion-ai-api"
33
33
  import FileSidebar from "../FileSidebar"
34
34
  import { findTargetFile } from "@/lib/utils/findTargetFile"
35
- import type { PackageFile } from "./CodeAndPreview"
35
+ import type { PackageFile } from "@/types/package"
36
36
  import { useShikiHighlighter } from "@/hooks/use-shiki-highlighter"
37
37
  import QuickOpen from "./QuickOpen"
38
38
  import GlobalFindReplace from "./GlobalFindReplace"
@@ -43,6 +43,7 @@ import {
43
43
  IDeleteFileResult,
44
44
  } from "@/hooks/useFileManagement"
45
45
  import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
46
+ import { inlineCopilot } from "codemirror-copilot"
46
47
 
47
48
  const defaultImports = `
48
49
  import React from "@types/react/jsx-runtime"
@@ -94,6 +95,7 @@ export const CodeEditor = ({
94
95
  // Get URL search params for file_path
95
96
  const urlParams = new URLSearchParams(window.location.search)
96
97
  const filePathFromUrl = urlParams.get("file_path")
98
+ const [aiAutocompleteEnabled, setAiAutocompleteEnabled] = useState(false)
97
99
 
98
100
  const entryPointFileName = useMemo(() => {
99
101
  const entryPointFile = findTargetFile(files, null)
@@ -324,12 +326,24 @@ export const CodeEditor = ({
324
326
  },
325
327
  }),
326
328
  ]
327
- if (codeCompletionApi?.apiKey) {
329
+ if (aiAutocompleteEnabled) {
328
330
  baseExtensions.push(
329
- // copilotPlugin({
330
- // apiKey: codeCompletionApi.apiKey,
331
- // language: Language.TYPESCRIPT,
332
- // }),
331
+ inlineCopilot(async (prefix, suffix) => {
332
+ const res = await fetch("/api/autocomplete/create_autocomplete", {
333
+ method: "POST",
334
+ headers: {
335
+ "Content-Type": "application/json",
336
+ },
337
+ body: JSON.stringify({
338
+ prefix,
339
+ suffix,
340
+ language: "typescript",
341
+ }),
342
+ })
343
+
344
+ const { prediction } = await res.json()
345
+ return prediction
346
+ }),
333
347
  EditorView.theme({
334
348
  ".cm-ghostText, .cm-ghostText *": {
335
349
  opacity: "0.6",
@@ -537,6 +551,7 @@ export const CodeEditor = ({
537
551
  Boolean(highlighter),
538
552
  isSaving,
539
553
  fontSize,
554
+ aiAutocompleteEnabled,
540
555
  ])
541
556
 
542
557
  const updateCurrentEditorContent = (newContent: string) => {
@@ -655,6 +670,10 @@ export const CodeEditor = ({
655
670
  files={Object.fromEntries(files.map((f) => [f.path, f.content]))}
656
671
  updateFileContent={updateFileContent}
657
672
  handleFileChange={handleFileChange}
673
+ aiAutocompleteState={[
674
+ aiAutocompleteEnabled,
675
+ setAiAutocompleteEnabled,
676
+ ]}
658
677
  />
659
678
  )}
660
679
  <div
@@ -26,6 +26,7 @@ import {
26
26
  TooltipProvider,
27
27
  TooltipTrigger,
28
28
  } from "@/components/ui/tooltip"
29
+ import ai from "fake-snippets-api/routes/api/ai"
29
30
 
30
31
  export type FileName = string
31
32
 
@@ -36,6 +37,7 @@ interface CodeEditorHeaderProps {
36
37
  fileSidebarState: ReturnType<typeof useState<boolean>>
37
38
  handleFileChange: (filename: FileName) => void
38
39
  entrypointFileName?: string
40
+ aiAutocompleteState: [boolean, React.Dispatch<React.SetStateAction<boolean>>]
39
41
  }
40
42
 
41
43
  export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
@@ -45,12 +47,13 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
45
47
  fileSidebarState,
46
48
  handleFileChange,
47
49
  entrypointFileName = "index.tsx",
50
+ aiAutocompleteState,
48
51
  }) => {
49
52
  const { Dialog: ImportPackageDialog, openDialog: openImportDialog } =
50
53
  useImportPackageDialog()
51
54
  const { toast } = useToast()
52
55
  const [sidebarOpen, setSidebarOpen] = fileSidebarState
53
- const [aiAutocompleteEnabled, setAiAutocompleteEnabled] = useState(false)
56
+ const [aiAutocompleteEnabled, setAiAutocompleteEnabled] = aiAutocompleteState
54
57
 
55
58
  const handleFormatFile = useCallback(() => {
56
59
  if (!window.prettier || !window.prettierPlugins) return
@@ -239,15 +242,16 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
239
242
  </DropdownMenuContent>
240
243
  </DropdownMenu>
241
244
  )}
245
+
242
246
  <TooltipProvider>
243
247
  <Tooltip>
244
248
  <TooltipTrigger asChild>
245
249
  <Button
246
250
  size="sm"
247
251
  variant="ghost"
248
- onClick={() =>
249
- setAiAutocompleteEnabled(!aiAutocompleteEnabled)
250
- }
252
+ onClick={() => {
253
+ setAiAutocompleteEnabled((prev) => !prev)
254
+ }}
251
255
  className={`relative bg-transparent ${aiAutocompleteEnabled ? "text-gray-600 bg-gray-50" : "text-gray-400"}`}
252
256
  >
253
257
  <Bot className="h-4 w-4" />
@@ -263,6 +267,7 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
263
267
  </TooltipContent>
264
268
  </Tooltip>
265
269
  </TooltipProvider>
270
+
266
271
  <Button size="sm" variant="ghost" onClick={() => openImportDialog()}>
267
272
  Import
268
273
  </Button>
@@ -18,19 +18,16 @@ import { Package } from "fake-snippets-api/lib/db/schema"
18
18
  import {
19
19
  ChevronDown,
20
20
  CodeIcon,
21
- Download,
22
21
  Edit2,
23
22
  Eye,
24
23
  EyeIcon,
25
24
  File,
26
25
  FilePenLine,
27
26
  MoreVertical,
28
- Package as PackageIcon,
29
27
  Pencil,
30
28
  Save,
31
29
  Share,
32
30
  Sidebar,
33
- Sparkles,
34
31
  Trash2,
35
32
  Undo2,
36
33
  } from "lucide-react"
@@ -41,7 +38,6 @@ import { useAxios } from "@/hooks/use-axios"
41
38
  import { useHotkeyCombo } from "@/hooks/use-hotkey"
42
39
  import { useToast } from "@/hooks/use-toast"
43
40
  import { useConfirmDeletePackageDialog } from "@/components/dialogs/confirm-delete-package-dialog"
44
- import { useFilesDialog } from "@/components/dialogs/files-dialog"
45
41
  import { useViewTsFilesDialog } from "@/components/dialogs/view-ts-files-dialog"
46
42
  import { DownloadButtonAndMenu } from "@/components/DownloadButtonAndMenu"
47
43
  import { TypeBadge } from "@/components/TypeBadge"
@@ -86,7 +82,6 @@ export default function EditorNav({
86
82
  } = useUpdatePackageDescriptionDialog()
87
83
  const { Dialog: DeleteDialog, openDialog: openDeleteDialog } =
88
84
  useConfirmDeletePackageDialog()
89
- const { Dialog: FilesDialog, openDialog: openFilesDialog } = useFilesDialog()
90
85
  const { Dialog: ViewTsFilesDialog, openDialog: openViewTsFilesDialog } =
91
86
  useViewTsFilesDialog()
92
87
 
@@ -376,13 +371,6 @@ export default function EditorNav({
376
371
  </Button>
377
372
  </DropdownMenuTrigger>
378
373
  <DropdownMenuContent>
379
- <DropdownMenuItem
380
- className="text-xs"
381
- onClick={() => openFilesDialog()}
382
- >
383
- <File className="mr-2 h-3 w-3" />
384
- View Files
385
- </DropdownMenuItem>
386
374
  <DropdownMenuItem
387
375
  className="text-xs"
388
376
  onClick={() => openupdateDescriptionDialog()}
@@ -395,7 +383,7 @@ export default function EditorNav({
395
383
  onClick={() => openViewTsFilesDialog()}
396
384
  >
397
385
  <File className="mr-2 h-3 w-3" />
398
- [Debug] View TS Files
386
+ View Files
399
387
  </DropdownMenuItem>
400
388
  <DropdownMenuSub>
401
389
  <DropdownMenuSubTrigger
@@ -566,7 +554,6 @@ export default function EditorNav({
566
554
  packageName={pkg?.unscoped_name ?? ""}
567
555
  packageOwner={pkg?.owner_github_username ?? ""}
568
556
  />
569
- <FilesDialog snippetId={pkg?.package_id ?? ""} />
570
557
  <ViewTsFilesDialog />
571
558
  </nav>
572
559
  )
@@ -23,7 +23,7 @@ import {
23
23
  TooltipProvider,
24
24
  TooltipTrigger,
25
25
  } from "@/components/ui/tooltip"
26
- import type { PackageFile } from "./CodeAndPreview"
26
+ import type { PackageFile } from "@/types/package"
27
27
  import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
28
28
 
29
29
  interface Match {
@@ -8,7 +8,7 @@ import {
8
8
  BookOpen,
9
9
  FileText,
10
10
  } from "lucide-react"
11
- import type { PackageFile } from "./CodeAndPreview"
11
+ import type { PackageFile } from "@/types/package"
12
12
  import { fuzzyMatch } from "../ViewPackagePage/utils/fuzz-search"
13
13
 
14
14
  interface ScoredFile extends PackageFile {
@@ -1,11 +1,11 @@
1
1
  import { useMemo } from "react"
2
2
 
3
3
  export const useCodeCompletionApi = () => {
4
- const codeiumApiKey = useMemo(() => {
4
+ const openrouterApiKey = useMemo(() => {
5
5
  return {
6
- apiKey: import.meta.env.VITE_CODIUM_API_KEY,
6
+ apiKey: import.meta.env.VITE_OPENROUTER_API_KEY,
7
7
  }
8
8
  }, [])
9
9
 
10
- return codeiumApiKey
10
+ return openrouterApiKey
11
11
  }
@@ -1,4 +1,4 @@
1
- import { useMutation } from "react-query"
1
+ import { useMutation, useQueryClient } from "react-query"
2
2
  import { useAxios } from "./use-axios"
3
3
  import { useToast } from "./use-toast"
4
4
 
@@ -7,6 +7,7 @@ export const useDeletePackage = ({
7
7
  }: { onSuccess?: () => void } = {}) => {
8
8
  const axios = useAxios()
9
9
  const { toast } = useToast()
10
+ const queryClient = useQueryClient()
10
11
 
11
12
  return useMutation(
12
13
  ["deletePackage"],
@@ -22,11 +23,14 @@ export const useDeletePackage = ({
22
23
  return response.data
23
24
  },
24
25
  {
25
- onSuccess: () => {
26
+ onSuccess: (_, variables) => {
26
27
  toast({
27
28
  title: "Package deleted",
28
29
  description: "Package deleted successfully",
29
30
  })
31
+ if (variables?.package_id) {
32
+ queryClient.invalidateQueries(["packages", variables.package_id])
33
+ }
30
34
  onSuccess?.()
31
35
  },
32
36
  onError: (error: any) => {
@@ -1,10 +1,10 @@
1
1
  import { useEffect, useMemo, useState, useCallback, useRef } from "react"
2
2
  import { isValidFileName } from "@/lib/utils/isValidFileName"
3
+ import { PackageFile } from "@/types/package"
3
4
  import {
4
5
  DEFAULT_CODE,
5
6
  generateRandomPackageName,
6
- PackageFile,
7
- } from "../components/package-port/CodeAndPreview"
7
+ } from "@/lib/utils/package-utils"
8
8
  import { Package } from "fake-snippets-api/lib/db/schema"
9
9
  import { usePackageFiles } from "./use-package-files"
10
10
  import { decodeUrlHashToText } from "@/lib/decodeUrlHashToText"
@@ -1,4 +1,4 @@
1
- import { PackageFile } from "@/components/package-port/CodeAndPreview"
1
+ import { PackageFile } from "@/types/package"
2
2
 
3
3
  export const findMainEntrypointFileFromTscircuitConfig = (
4
4
  files: PackageFile[],
@@ -0,0 +1,10 @@
1
+ export const DEFAULT_CODE = `
2
+ export default () => (
3
+ <board width="10mm" height="10mm">
4
+ {/* write your code here! */}
5
+ </board>
6
+ )
7
+ `.trim()
8
+
9
+ export const generateRandomPackageName = () =>
10
+ `untitled-package-${Math.floor(Math.random() * 900) + 100}`
@@ -61,6 +61,11 @@ export const DashboardPage = () => {
61
61
  const response = await axios.get("/packages/list_trending")
62
62
  return response.data.packages
63
63
  },
64
+ {
65
+ refetchOnWindowFocus: false,
66
+ refetchOnMount: false,
67
+ refetchOnReconnect: false,
68
+ },
64
69
  )
65
70
 
66
71
  const { data: latestPackages } = useQuery<Package[]>(
@@ -69,6 +74,11 @@ export const DashboardPage = () => {
69
74
  const response = await axios.get("/packages/list_latest")
70
75
  return response.data.packages
71
76
  },
77
+ {
78
+ refetchOnWindowFocus: false,
79
+ refetchOnMount: false,
80
+ refetchOnReconnect: false,
81
+ },
72
82
  )
73
83
 
74
84
  const baseUrl = useSnippetsBaseApiUrl()
@@ -0,0 +1,4 @@
1
+ export interface PackageFile {
2
+ path: string
3
+ content: string
4
+ }