@tscircuit/fake-snippets 0.0.115 → 0.0.116

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.js CHANGED
@@ -593,7 +593,7 @@ var seed = (db) => {
593
593
  phone: "555-123-4567"
594
594
  }
595
595
  });
596
- db.addAccount({
596
+ const seveibarAcc = db.addAccount({
597
597
  github_username: "seveibar"
598
598
  });
599
599
  if (process.env.AUTOLOAD_PACKAGES === "true") {
@@ -2370,6 +2370,10 @@ exports.TestComponent = TestComponent;
2370
2370
  github_handle: "tscircuit",
2371
2371
  is_personal_org: true
2372
2372
  });
2373
+ db.addOrganizationAccount({
2374
+ org_id: testOrg.org_id,
2375
+ account_id: seveibarAcc.account_id
2376
+ });
2373
2377
  };
2374
2378
 
2375
2379
  // fake-snippets-api/lib/package_file/generate-fs-sha.ts
@@ -18,7 +18,7 @@ export const seed = (db: DbClient) => {
18
18
  phone: "555-123-4567",
19
19
  },
20
20
  })
21
- db.addAccount({
21
+ const seveibarAcc = db.addAccount({
22
22
  github_username: "seveibar",
23
23
  })
24
24
 
@@ -1835,4 +1835,9 @@ exports.TestComponent = TestComponent;
1835
1835
  github_handle: "tscircuit",
1836
1836
  is_personal_org: true,
1837
1837
  })
1838
+
1839
+ db.addOrganizationAccount({
1840
+ org_id: testOrg.org_id,
1841
+ account_id: seveibarAcc.account_id,
1842
+ })
1838
1843
  }
@@ -0,0 +1,16 @@
1
+ import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
2
+ import { z } from "zod"
3
+
4
+ export default withRouteSpec({
5
+ methods: ["POST"],
6
+ auth: "session",
7
+ jsonBody: z.object({
8
+ prefix: z.string(),
9
+ suffix: z.string(),
10
+ }),
11
+ jsonResponse: z.object({
12
+ prediction: z.string(),
13
+ }),
14
+ })(async (req, ctx) => {
15
+ return ctx.json({ prediction: "{/* mock autocomplete prediction */}" })
16
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.115",
3
+ "version": "0.0.116",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -81,7 +81,7 @@
81
81
  "@tscircuit/3d-viewer": "^0.0.407",
82
82
  "@tscircuit/assembly-viewer": "^0.0.5",
83
83
  "@tscircuit/create-snippet-url": "^0.0.8",
84
- "@tscircuit/eval": "^0.0.325",
84
+ "@tscircuit/eval": "^0.0.406",
85
85
  "@tscircuit/layout": "^0.0.29",
86
86
  "@tscircuit/mm": "^0.0.8",
87
87
  "@tscircuit/pcb-viewer": "^1.11.218",
@@ -14,7 +14,7 @@ export const GithubAvatarWithFallback = ({
14
14
  size?: number
15
15
  }) => {
16
16
  return (
17
- <Avatar className={`border-4 border-gray-100 ${className}`}>
17
+ <Avatar className={className}>
18
18
  <AvatarImage
19
19
  src={`https://github.com/${username}.png${size ? `?size=${size}` : ""}`}
20
20
  alt={`${username || fallback} avatar`}
@@ -4,7 +4,7 @@ import { Building2, Users, Package, Lock, Globe2, Settings } from "lucide-react"
4
4
  import { cn } from "@/lib/utils"
5
5
  import { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
6
6
  import { useGlobalStore } from "@/hooks/use-global-store"
7
- import { useLocation } from "wouter"
7
+ import { Link, useLocation } from "wouter"
8
8
  import { useOrganization } from "@/hooks/use-organization"
9
9
  import { GithubAvatarWithFallback } from "../GithubAvatarWithFallback"
10
10
 
@@ -97,9 +97,12 @@ export const OrganizationHeader: React.FC<OrganizationHeaderProps> = ({
97
97
 
98
98
  <div className="flex-1 min-w-0">
99
99
  <div className="flex items-center justify-between mb-3">
100
- <h1 className="font-bold text-gray-900 text-2xl md:text-3xl truncate">
100
+ <Link
101
+ href={`/${organization.name}`}
102
+ className="font-bold text-gray-900 text-2xl md:text-3xl truncate"
103
+ >
101
104
  {organization.name}
102
- </h1>
105
+ </Link>
103
106
  {canManageOrg && showActions && (
104
107
  <Button variant="outline" onClick={handleSettingsClick}>
105
108
  <Settings className="mr-2 h-4 w-4" />
@@ -1,48 +1,26 @@
1
1
  import React from "react"
2
2
  import { Link } from "wouter"
3
3
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
4
- import { Badge } from "@/components/ui/badge"
5
- import { Users, Crown, Shield, User, Loader2 } from "lucide-react"
4
+ import { Users, Loader2 } from "lucide-react"
6
5
  import { timeAgo } from "@/lib/utils/timeAgo"
7
6
  import { cn } from "@/lib/utils"
8
7
  import { useListOrgMembers } from "@/hooks/use-list-org-members"
8
+ import { getMemberRole } from "@/lib/utils/member-role"
9
+ import { RoleBadge } from "@/components/ui/role-badge"
10
+ import { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
9
11
 
10
12
  interface OrganizationMembersProps {
11
- orgId: string
13
+ organization: PublicOrgSchema
12
14
  className?: string
13
15
  }
14
- type MemberRole = "owner" | "admin" | "member" //todo
15
- const getRoleIcon = (role: MemberRole) => {
16
- switch (role) {
17
- case "owner":
18
- return <Crown className="h-3 w-3" />
19
- case "admin":
20
- return <Shield className="h-3 w-3" />
21
- case "member":
22
- return <User className="h-3 w-3" />
23
- default:
24
- return <User className="h-3 w-3" />
25
- }
26
- }
27
-
28
- const getRoleColor = (role: MemberRole) => {
29
- switch (role) {
30
- case "owner":
31
- return "bg-yellow-100 text-yellow-800 border-yellow-200"
32
- case "admin":
33
- return "bg-purple-100 text-purple-800 border-purple-200"
34
- case "member":
35
- return "bg-gray-100 text-gray-800 border-gray-200"
36
- default:
37
- return "bg-gray-100 text-gray-800 border-gray-200"
38
- }
39
- }
40
16
 
41
17
  export const OrganizationMembers: React.FC<OrganizationMembersProps> = ({
42
- orgId,
18
+ organization,
43
19
  className,
44
20
  }) => {
45
- const { data: members = [], isLoading } = useListOrgMembers({ orgId })
21
+ const { data: members = [], isLoading } = useListOrgMembers({
22
+ orgId: organization.org_id,
23
+ })
46
24
 
47
25
  if (isLoading) {
48
26
  return (
@@ -78,60 +56,54 @@ export const OrganizationMembers: React.FC<OrganizationMembersProps> = ({
78
56
  </div>
79
57
 
80
58
  <div className="space-y-2 sm:space-y-3">
81
- {members.map((member) => (
82
- <Link
83
- key={member.account_id || member.github_username}
84
- href={`/${member.github_username}`}
85
- className="block"
86
- >
87
- <div className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50 transition-colors cursor-pointer">
88
- <div className="flex items-center gap-3 min-w-0 flex-1">
89
- <Avatar className="h-10 w-10 sm:h-12 sm:w-12 flex-shrink-0">
90
- <AvatarImage
91
- src={`https://github.com/${member.github_username}.png`}
92
- alt={`${member.github_username} avatar`}
93
- />
94
- <AvatarFallback className="text-sm font-medium">
95
- {member.github_username
96
- .split(" ")
97
- .map((word) => word[0])
98
- .join("")
99
- .toUpperCase()
100
- .slice(0, 2)}
101
- </AvatarFallback>
102
- </Avatar>
59
+ {members.map((member) => {
60
+ const role = getMemberRole(organization, member.account_id)
61
+ return (
62
+ <Link
63
+ key={member.account_id || member.github_username}
64
+ href={`/${member.github_username}`}
65
+ className="block"
66
+ >
67
+ <div className="flex items-center justify-between p-3 rounded-lg hover:bg-gray-50 transition-colors cursor-pointer">
68
+ <div className="flex items-center gap-3 min-w-0 flex-1">
69
+ <Avatar className="h-10 w-10 sm:h-12 sm:w-12 flex-shrink-0">
70
+ <AvatarImage
71
+ src={`https://github.com/${member.github_username}.png`}
72
+ alt={`${member.github_username} avatar`}
73
+ />
74
+ <AvatarFallback className="text-sm font-medium">
75
+ {member.github_username
76
+ .split(" ")
77
+ .map((word) => word[0])
78
+ .join("")
79
+ .toUpperCase()
80
+ .slice(0, 2)}
81
+ </AvatarFallback>
82
+ </Avatar>
103
83
 
104
- <div className="min-w-0 flex-1">
105
- <div className="flex items-center gap-2 mb-1">
106
- <h3 className="font-medium text-gray-900 truncate">
107
- {member.github_username}
108
- </h3>
109
- <Badge
110
- variant="outline"
111
- className={cn(
112
- "text-xs flex items-center gap-1 flex-shrink-0",
113
- getRoleColor("admin"),
114
- )}
115
- >
116
- {getRoleIcon("admin")}
117
- {"admin"}
118
- </Badge>
84
+ <div className="min-w-0 flex-1">
85
+ <div className="flex items-center gap-2 mb-1">
86
+ <h3 className="font-medium text-gray-900 truncate">
87
+ {member.github_username}
88
+ </h3>
89
+ <RoleBadge role={role} />
90
+ </div>
91
+ <p className="text-sm text-gray-500 truncate">
92
+ @{member.github_username}
93
+ </p>
119
94
  </div>
120
- <p className="text-sm text-gray-500 truncate">
121
- @{member.github_username}
122
- </p>
123
95
  </div>
96
+ {member.joined_at && (
97
+ <div className="text-right flex-shrink-0 hidden sm:block">
98
+ <p className="text-xs text-gray-500">
99
+ Joined {timeAgo(new Date(member.joined_at))}
100
+ </p>
101
+ </div>
102
+ )}
124
103
  </div>
125
- {member.joined_at && (
126
- <div className="text-right flex-shrink-0 hidden sm:block">
127
- <p className="text-xs text-gray-500">
128
- Joined {timeAgo(new Date(member.joined_at))}
129
- </p>
130
- </div>
131
- )}
132
- </div>
133
- </Link>
134
- ))}
104
+ </Link>
105
+ )
106
+ })}
135
107
  </div>
136
108
 
137
109
  {members.length === 0 && (
@@ -51,6 +51,7 @@ import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
51
51
  import { inlineCopilot } from "codemirror-copilot"
52
52
  import { useViewTsFilesDialog } from "@/components/dialogs/view-ts-files-dialog"
53
53
  import { Loader2 } from "lucide-react"
54
+ import { useAxios } from "@/hooks/use-axios"
54
55
 
55
56
  const defaultImports = `
56
57
  import React from "@types/react/jsx-runtime"
@@ -111,7 +112,7 @@ export const CodeEditor = ({
111
112
  const highlightTimeoutRef = useRef<number | null>(null)
112
113
 
113
114
  const { highlighter } = useShikiHighlighter()
114
-
115
+ const axios = useAxios()
115
116
  // Get URL search params for file_path
116
117
  const urlParams = new URLSearchParams(window.location.search)
117
118
  const filePathFromUrl = urlParams.get("file_path")
@@ -358,23 +359,12 @@ export const CodeEditor = ({
358
359
  if (aiAutocompleteEnabled) {
359
360
  baseExtensions.push(
360
361
  inlineCopilot(async (prefix, suffix) => {
361
- const res = await fetch(
362
- `${apiUrl}/autocomplete/create_autocomplete`,
363
- {
364
- method: "POST",
365
- headers: {
366
- "Content-Type": "application/json",
367
- },
368
- body: JSON.stringify({
369
- prefix,
370
- suffix,
371
- language: "typescript",
372
- }),
373
- },
374
- )
375
-
376
- const { prediction } = await res.json()
377
- return prediction
362
+ const res = await axios.post("/autocomplete/create_autocomplete", {
363
+ prefix,
364
+ suffix,
365
+ language: "typescript",
366
+ })
367
+ return res.data?.prediction
378
368
  }),
379
369
  EditorView.theme({
380
370
  ".cm-ghostText, .cm-ghostText *": {
@@ -390,12 +390,12 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
390
390
  onClick={() => {
391
391
  setAiAutocompleteEnabled((prev) => !prev)
392
392
  }}
393
- className={`relative group bg-transparent ${aiAutocompleteEnabled ? "text-gray-600 bg-gray-50" : "text-gray-400"}`}
393
+ className={`relative group bg-transparent ${aiAutocompleteEnabled ? "text-black bg-white" : "text-gray-500 group-hover:text-black"}`}
394
394
  >
395
395
  <Bot className="h-4 w-4" />
396
396
  {!aiAutocompleteEnabled && (
397
397
  <div className="absolute inset-0 flex items-center justify-center">
398
- <div className="w-5 h-0.5 group-hover:bg-slate-900 bg-gray-400 rotate-45 rounded-full" />
398
+ <div className="w-5 h-0.5 group-hover:bg-black bg-gray-500 rotate-45 rounded-full" />
399
399
  </div>
400
400
  )}
401
401
  </Button>
@@ -0,0 +1,27 @@
1
+ import { cn } from "@/lib/utils"
2
+ import {
3
+ type MemberRole,
4
+ getRoleIcon,
5
+ getRoleColor,
6
+ getRoleName,
7
+ } from "@/lib/utils/member-role"
8
+
9
+ interface RoleBadgeProps {
10
+ role: MemberRole
11
+ className?: string
12
+ }
13
+
14
+ export const RoleBadge = ({ role, className }: RoleBadgeProps) => {
15
+ return (
16
+ <div
17
+ className={cn(
18
+ "select-none inline-flex items-center gap-1 py-0.5 px-1.5 rounded text-xs font-medium flex-shrink-0",
19
+ getRoleColor(role),
20
+ className,
21
+ )}
22
+ >
23
+ {getRoleIcon(role)}
24
+ <span className="leading-none">{getRoleName(role)}</span>
25
+ </div>
26
+ )
27
+ }
@@ -25,6 +25,7 @@ export const useListOrgMembers = ({
25
25
  enabled: Boolean(orgId || orgName),
26
26
  retry: false,
27
27
  refetchOnWindowFocus: false,
28
+ refetchOnMount: false,
28
29
  keepPreviousData: true,
29
30
  },
30
31
  )
@@ -1,8 +1,11 @@
1
- import { useQuery } from "react-query"
1
+ import { useQuery, UseQueryOptions } from "react-query"
2
2
  import { useAxios } from "@/hooks/use-axios"
3
3
  import type { Package } from "fake-snippets-api/lib/db/schema"
4
4
 
5
- export const usePackageByName = (packageName: string | null) => {
5
+ export const usePackageByName = (
6
+ packageName: string | null,
7
+ options?: UseQueryOptions<Package, Error & { status: number }>,
8
+ ) => {
6
9
  const axios = useAxios()
7
10
  return useQuery<Package, Error & { status: number }>(
8
11
  ["package", packageName],
@@ -19,6 +22,8 @@ export const usePackageByName = (packageName: string | null) => {
19
22
  retry: false,
20
23
  enabled: Boolean(packageName),
21
24
  refetchOnWindowFocus: false,
25
+ staleTime: 5 * 60 * 1000,
26
+ ...options,
22
27
  },
23
28
  )
24
29
  }
@@ -1,5 +1,7 @@
1
1
  import { useMutation, useQuery, useQueryClient } from "react-query"
2
2
  import { useAxios } from "./use-axios"
3
+ import { usePackageByName } from "./use-package-by-package-name"
4
+ import { usePackageById } from "./use-package-by-package-id"
3
5
 
4
6
  type PackageStarQuery = { package_id: string } | { name: string }
5
7
 
@@ -9,7 +11,11 @@ interface PackageStarResponse {
9
11
  }
10
12
 
11
13
  export const usePackageStars = (query: PackageStarQuery | null) => {
12
- const axios = useAxios()
14
+ const packageName = query && "name" in query ? query.name : null
15
+ const packageId = query && "package_id" in query ? query.package_id : null
16
+ const packageQuery = packageName
17
+ ? usePackageByName(packageName)
18
+ : usePackageById(packageId)
13
19
 
14
20
  return useQuery<PackageStarResponse, Error & { status: number }>(
15
21
  ["packageStars", query],
@@ -17,19 +23,14 @@ export const usePackageStars = (query: PackageStarQuery | null) => {
17
23
  if (!query) {
18
24
  throw new Error("Query is required")
19
25
  }
20
-
21
- const { data } = await axios.get("/packages/get", {
22
- params: query,
23
- })
24
-
25
26
  return {
26
- is_starred: data.package.is_starred ?? false,
27
- star_count: data.package.star_count ?? 0,
27
+ is_starred: packageQuery.data?.is_starred ?? false,
28
+ star_count: packageQuery.data?.star_count ?? 0,
28
29
  }
29
30
  },
30
31
  {
31
32
  retry: false,
32
- enabled: Boolean(query),
33
+ enabled: Boolean(query) && packageQuery.isSuccess,
33
34
  },
34
35
  )
35
36
  }
@@ -0,0 +1,61 @@
1
+ import { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
2
+ import { Crown, Shield, User } from "lucide-react"
3
+
4
+ export type MemberRole = "owner" | "manager" | "member"
5
+
6
+ export const getRoleName = (role: MemberRole) => {
7
+ return role.charAt(0).toUpperCase() + role.slice(1)
8
+ }
9
+
10
+ export const getMemberRole = (
11
+ organization: PublicOrgSchema,
12
+ accountId: string,
13
+ ): MemberRole => {
14
+ const isOwner =
15
+ String(accountId).trim() === String(organization.owner_account_id).trim()
16
+ const isManager = Object.values(organization.user_permissions ?? {}).some(
17
+ Boolean,
18
+ )
19
+ if (isOwner) {
20
+ return "owner"
21
+ }
22
+ if (isManager) {
23
+ return "manager"
24
+ }
25
+ return "member"
26
+ }
27
+
28
+ export const getRoleIcon = (role: MemberRole) => {
29
+ switch (role) {
30
+ case "owner":
31
+ return <Crown className="size-3" />
32
+ case "manager":
33
+ return <Shield className="size-3" />
34
+ case "member":
35
+ return <User className="size-3" />
36
+ default:
37
+ return <User className="size-3" />
38
+ }
39
+ }
40
+
41
+ export const getRoleColor = (role: MemberRole) => {
42
+ switch (role) {
43
+ case "owner":
44
+ return "bg-amber-100 text-amber-800 border-amber-200"
45
+ case "manager":
46
+ return "bg-blue-50 text-blue-700 border border-blue-200"
47
+ default:
48
+ return "bg-gray-100 text-gray-800 border-gray-200"
49
+ }
50
+ }
51
+
52
+ export const getRoleDescription = (role: MemberRole) => {
53
+ switch (role) {
54
+ case "owner":
55
+ return "Full access to organization settings"
56
+ case "manager":
57
+ return "Limited administrative privileges"
58
+ case "member":
59
+ return "Standard member access"
60
+ }
61
+ }
@@ -14,7 +14,6 @@ import {
14
14
  SelectTrigger,
15
15
  SelectValue,
16
16
  } from "@/components/ui/select"
17
- import { useListOrgMembers } from "@/hooks/use-list-org-members"
18
17
  import { useOrgByGithubHandle } from "@/hooks/use-org-by-github-handle"
19
18
  import { PackageCardSkeleton } from "@/components/PackageCardSkeleton"
20
19
  import { useApiBaseUrl } from "@/hooks/use-packages-base-api-url"
@@ -175,7 +174,7 @@ export const OrganizationProfilePageContent = ({
175
174
 
176
175
  {activeTab === "members" && (
177
176
  <div>
178
- <OrganizationMembers orgId={org.org_id} />
177
+ <OrganizationMembers organization={org} />
179
178
  </div>
180
179
  )}
181
180
  </div>