@tscircuit/fake-snippets 0.0.100 → 0.0.101
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/bun.lock +2 -2
- package/dist/bundle.js +456 -411
- package/dist/index.d.ts +4 -2
- package/dist/index.js +25 -0
- package/fake-snippets-api/lib/db/db-client.ts +32 -0
- package/fake-snippets-api/routes/api/accounts/search.ts +20 -0
- package/package.json +2 -2
- package/src/components/CmdKMenu.tsx +154 -19
- package/src/components/Header2.tsx +106 -25
- package/src/components/PackageBuildsPage/package-build-header.tsx +19 -15
- package/src/components/ViewPackagePage/components/important-files-view.tsx +18 -13
- package/src/components/package-port/CodeEditorHeader.tsx +20 -16
- package/src/pages/landing.tsx +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1073,7 +1073,7 @@ declare const createDatabase: ({ seed }?: {
|
|
|
1073
1073
|
datasheet_pdf_urls: string[] | null;
|
|
1074
1074
|
ai_description: string | null;
|
|
1075
1075
|
}[];
|
|
1076
|
-
}, "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" | "getDatasheetByChipName" | "listDatasheets" | "updateDatasheet"> & {
|
|
1076
|
+
}, "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" | "searchAccounts" | "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" | "getDatasheetByChipName" | "listDatasheets" | "updateDatasheet"> & {
|
|
1077
1077
|
addOrder: (order: Omit<Order, "order_id">) => Order;
|
|
1078
1078
|
getOrderById: (orderId: string) => Order | undefined;
|
|
1079
1079
|
getOrderFilesByOrderId: (orderId: string) => OrderFile[];
|
|
@@ -1121,6 +1121,7 @@ declare const createDatabase: ({ seed }?: {
|
|
|
1121
1121
|
}) => Snippet | undefined;
|
|
1122
1122
|
searchSnippets: (query: string) => Snippet[];
|
|
1123
1123
|
searchPackages: (query: string) => Package[];
|
|
1124
|
+
searchAccounts: (query: string, limit?: number) => Account[];
|
|
1124
1125
|
deleteSnippet: (snippetId: string) => boolean;
|
|
1125
1126
|
addSession: (session: Omit<Session, "session_id">) => Session;
|
|
1126
1127
|
getSessions: ({ account_id, is_cli_session, }: {
|
|
@@ -1426,7 +1427,7 @@ declare const createDatabase: ({ seed }?: {
|
|
|
1426
1427
|
datasheet_pdf_urls: string[] | null;
|
|
1427
1428
|
ai_description: string | null;
|
|
1428
1429
|
}[];
|
|
1429
|
-
}, "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" | "getDatasheetByChipName" | "listDatasheets" | "updateDatasheet"> & {
|
|
1430
|
+
}, "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" | "searchAccounts" | "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" | "getDatasheetByChipName" | "listDatasheets" | "updateDatasheet"> & {
|
|
1430
1431
|
addOrder: (order: Omit<Order, "order_id">) => Order;
|
|
1431
1432
|
getOrderById: (orderId: string) => Order | undefined;
|
|
1432
1433
|
getOrderFilesByOrderId: (orderId: string) => OrderFile[];
|
|
@@ -1474,6 +1475,7 @@ declare const createDatabase: ({ seed }?: {
|
|
|
1474
1475
|
}) => Snippet | undefined;
|
|
1475
1476
|
searchSnippets: (query: string) => Snippet[];
|
|
1476
1477
|
searchPackages: (query: string) => Package[];
|
|
1478
|
+
searchAccounts: (query: string, limit?: number) => Account[];
|
|
1477
1479
|
deleteSnippet: (snippetId: string) => boolean;
|
|
1478
1480
|
addSession: (session: Omit<Session, "session_id">) => Session;
|
|
1479
1481
|
getSessions: ({ account_id, is_cli_session, }: {
|
package/dist/index.js
CHANGED
|
@@ -2798,6 +2798,31 @@ var initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
2798
2798
|
];
|
|
2799
2799
|
return matchingPackages;
|
|
2800
2800
|
},
|
|
2801
|
+
searchAccounts: (query, limit) => {
|
|
2802
|
+
const state = get();
|
|
2803
|
+
const lowercaseQuery = query.toLowerCase();
|
|
2804
|
+
const accountsWithPublicPackages = /* @__PURE__ */ new Set();
|
|
2805
|
+
state.packages.filter((pkg) => pkg.is_public === true).forEach((pkg) => {
|
|
2806
|
+
if (pkg.creator_account_id) {
|
|
2807
|
+
accountsWithPublicPackages.add(pkg.creator_account_id);
|
|
2808
|
+
}
|
|
2809
|
+
if (pkg.owner_github_username) {
|
|
2810
|
+
const account = state.accounts.find(
|
|
2811
|
+
(acc) => acc.github_username === pkg.owner_github_username
|
|
2812
|
+
);
|
|
2813
|
+
if (account) {
|
|
2814
|
+
accountsWithPublicPackages.add(account.account_id);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
});
|
|
2818
|
+
const matchingAccounts = state.accounts.filter((account) => {
|
|
2819
|
+
if (!accountsWithPublicPackages.has(account.account_id)) {
|
|
2820
|
+
return false;
|
|
2821
|
+
}
|
|
2822
|
+
return account.github_username.toLowerCase().includes(lowercaseQuery);
|
|
2823
|
+
}).slice(0, limit || 50);
|
|
2824
|
+
return matchingAccounts;
|
|
2825
|
+
},
|
|
2801
2826
|
deleteSnippet: (snippetId) => {
|
|
2802
2827
|
let deleted = false;
|
|
2803
2828
|
set((state) => {
|
|
@@ -1019,6 +1019,38 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
1019
1019
|
|
|
1020
1020
|
return matchingPackages
|
|
1021
1021
|
},
|
|
1022
|
+
searchAccounts: (query: string, limit?: number): Account[] => {
|
|
1023
|
+
const state = get()
|
|
1024
|
+
const lowercaseQuery = query.toLowerCase()
|
|
1025
|
+
|
|
1026
|
+
const accountsWithPublicPackages = new Set<string>()
|
|
1027
|
+
state.packages
|
|
1028
|
+
.filter((pkg) => pkg.is_public === true)
|
|
1029
|
+
.forEach((pkg) => {
|
|
1030
|
+
if (pkg.creator_account_id) {
|
|
1031
|
+
accountsWithPublicPackages.add(pkg.creator_account_id)
|
|
1032
|
+
}
|
|
1033
|
+
if (pkg.owner_github_username) {
|
|
1034
|
+
const account = state.accounts.find(
|
|
1035
|
+
(acc) => acc.github_username === pkg.owner_github_username,
|
|
1036
|
+
)
|
|
1037
|
+
if (account) {
|
|
1038
|
+
accountsWithPublicPackages.add(account.account_id)
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
})
|
|
1042
|
+
|
|
1043
|
+
const matchingAccounts = state.accounts
|
|
1044
|
+
.filter((account) => {
|
|
1045
|
+
if (!accountsWithPublicPackages.has(account.account_id)) {
|
|
1046
|
+
return false
|
|
1047
|
+
}
|
|
1048
|
+
return account.github_username.toLowerCase().includes(lowercaseQuery)
|
|
1049
|
+
})
|
|
1050
|
+
.slice(0, limit || 50)
|
|
1051
|
+
|
|
1052
|
+
return matchingAccounts
|
|
1053
|
+
},
|
|
1022
1054
|
deleteSnippet: (snippetId: string): boolean => {
|
|
1023
1055
|
let deleted = false
|
|
1024
1056
|
set((state) => {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { accountSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
|
|
5
|
+
export default withRouteSpec({
|
|
6
|
+
methods: ["POST"],
|
|
7
|
+
auth: "session",
|
|
8
|
+
jsonBody: z.object({
|
|
9
|
+
query: z.string(),
|
|
10
|
+
limit: z.number().optional().default(50),
|
|
11
|
+
}),
|
|
12
|
+
jsonResponse: z.object({
|
|
13
|
+
ok: z.boolean(),
|
|
14
|
+
accounts: z.array(accountSchema),
|
|
15
|
+
}),
|
|
16
|
+
})(async (req, ctx) => {
|
|
17
|
+
const { query, limit } = req.jsonBody
|
|
18
|
+
const accounts = ctx.db.searchAccounts(query, limit)
|
|
19
|
+
return ctx.json({ accounts, ok: true })
|
|
20
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/fake-snippets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.101",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@tscircuit/mm": "^0.0.8",
|
|
84
84
|
"@tscircuit/pcb-viewer": "^1.11.194",
|
|
85
85
|
"@tscircuit/prompt-benchmarks": "^0.0.28",
|
|
86
|
-
"@tscircuit/runframe": "0.0.
|
|
86
|
+
"@tscircuit/runframe": "0.0.736",
|
|
87
87
|
"@tscircuit/schematic-viewer": "^2.0.21",
|
|
88
88
|
"@types/babel__standalone": "^7.1.7",
|
|
89
89
|
"@types/bun": "^1.1.10",
|
|
@@ -5,7 +5,7 @@ import { useHotkeyCombo } from "@/hooks/use-hotkey"
|
|
|
5
5
|
import { useNotImplementedToast } from "@/hooks/use-toast"
|
|
6
6
|
import { fuzzyMatch } from "@/components/ViewPackagePage/utils/fuzz-search"
|
|
7
7
|
import { Command } from "cmdk"
|
|
8
|
-
import { Package } from "fake-snippets-api/lib/db/schema"
|
|
8
|
+
import { Package, Account } from "fake-snippets-api/lib/db/schema"
|
|
9
9
|
import React, { useCallback, useEffect, useMemo, useRef } from "react"
|
|
10
10
|
import { useQuery } from "react-query"
|
|
11
11
|
import {
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
Sparkles,
|
|
17
17
|
Clock,
|
|
18
18
|
ArrowRight,
|
|
19
|
+
Star,
|
|
20
|
+
User,
|
|
19
21
|
} from "lucide-react"
|
|
20
22
|
import { DialogTitle, DialogDescription } from "@/components/ui/dialog"
|
|
21
23
|
|
|
@@ -40,6 +42,11 @@ interface ScoredPackage extends Package {
|
|
|
40
42
|
matches: number[]
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
interface ScoredAccount extends Account {
|
|
46
|
+
score: number
|
|
47
|
+
matches: number[]
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
const CmdKMenu = () => {
|
|
44
51
|
const [open, setOpen] = React.useState(false)
|
|
45
52
|
const [searchQuery, setSearchQuery] = React.useState("")
|
|
@@ -113,13 +120,42 @@ const CmdKMenu = () => {
|
|
|
113
120
|
["packageSearch", searchQuery],
|
|
114
121
|
async () => {
|
|
115
122
|
if (!searchQuery) return []
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
123
|
+
try {
|
|
124
|
+
const { data } = await axios.post("/packages/search", {
|
|
125
|
+
query: searchQuery,
|
|
126
|
+
})
|
|
127
|
+
return data.packages || []
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.warn("Failed to fetch packages:", error)
|
|
130
|
+
return []
|
|
131
|
+
}
|
|
120
132
|
},
|
|
121
133
|
{
|
|
122
134
|
enabled: Boolean(searchQuery),
|
|
135
|
+
retry: false,
|
|
136
|
+
refetchOnWindowFocus: false,
|
|
137
|
+
},
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
const { data: allAccounts = [], isLoading: isSearchingAccounts } = useQuery(
|
|
141
|
+
["accountSearch", searchQuery],
|
|
142
|
+
async () => {
|
|
143
|
+
if (!searchQuery) return []
|
|
144
|
+
try {
|
|
145
|
+
const { data } = await axios.post("/accounts/search", {
|
|
146
|
+
query: searchQuery,
|
|
147
|
+
limit: 5,
|
|
148
|
+
})
|
|
149
|
+
return data.accounts || []
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.warn("Failed to fetch accounts:", error)
|
|
152
|
+
return []
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
enabled: Boolean(searchQuery) && Boolean(currentUser),
|
|
157
|
+
retry: false,
|
|
158
|
+
refetchOnWindowFocus: false,
|
|
123
159
|
},
|
|
124
160
|
)
|
|
125
161
|
|
|
@@ -133,20 +169,43 @@ const CmdKMenu = () => {
|
|
|
133
169
|
})
|
|
134
170
|
.filter((pkg: ScoredPackage) => pkg.score >= 0)
|
|
135
171
|
.sort((a: ScoredPackage, b: ScoredPackage) => b.score - a.score)
|
|
136
|
-
.slice(0,
|
|
172
|
+
.slice(0, 6)
|
|
137
173
|
}, [allPackages, searchQuery])
|
|
138
174
|
|
|
175
|
+
const accountSearchResults = useMemo((): ScoredAccount[] => {
|
|
176
|
+
if (!searchQuery || !allAccounts.length) return []
|
|
177
|
+
|
|
178
|
+
return allAccounts
|
|
179
|
+
.map((account: Account) => {
|
|
180
|
+
const { score, matches } = fuzzyMatch(
|
|
181
|
+
searchQuery,
|
|
182
|
+
account.github_username,
|
|
183
|
+
)
|
|
184
|
+
return { ...account, score, matches }
|
|
185
|
+
})
|
|
186
|
+
.filter((account: ScoredAccount) => account.score >= 0)
|
|
187
|
+
.sort((a: ScoredAccount, b: ScoredAccount) => b.score - a.score)
|
|
188
|
+
.slice(0, 5)
|
|
189
|
+
}, [allAccounts, searchQuery])
|
|
190
|
+
|
|
139
191
|
const { data: recentPackages = [] } = useQuery<Package[]>(
|
|
140
192
|
["userPackages", currentUser],
|
|
141
193
|
async () => {
|
|
142
194
|
if (!currentUser) return []
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
195
|
+
try {
|
|
196
|
+
const response = await axios.post(`/packages/list`, {
|
|
197
|
+
owner_github_username: currentUser,
|
|
198
|
+
})
|
|
199
|
+
return response.data.packages || []
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.warn("Failed to fetch recent packages:", error)
|
|
202
|
+
return []
|
|
203
|
+
}
|
|
147
204
|
},
|
|
148
205
|
{
|
|
149
206
|
enabled: !!currentUser && !searchQuery,
|
|
207
|
+
retry: false,
|
|
208
|
+
refetchOnWindowFocus: false,
|
|
150
209
|
},
|
|
151
210
|
)
|
|
152
211
|
|
|
@@ -195,7 +254,7 @@ const CmdKMenu = () => {
|
|
|
195
254
|
|
|
196
255
|
const allItems = useMemo(() => {
|
|
197
256
|
const items: Array<{
|
|
198
|
-
type: "package" | "recent" | "template" | "blank" | "import"
|
|
257
|
+
type: "package" | "account" | "recent" | "template" | "blank" | "import"
|
|
199
258
|
item: any
|
|
200
259
|
disabled?: boolean
|
|
201
260
|
}> = []
|
|
@@ -206,6 +265,12 @@ const CmdKMenu = () => {
|
|
|
206
265
|
})
|
|
207
266
|
}
|
|
208
267
|
|
|
268
|
+
if (searchQuery && accountSearchResults.length > 0) {
|
|
269
|
+
accountSearchResults.forEach((account) => {
|
|
270
|
+
items.push({ type: "account", item: account })
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
|
|
209
274
|
if (!searchQuery && recentPackages.length > 0) {
|
|
210
275
|
recentPackages.slice(0, 6).forEach((pkg) => {
|
|
211
276
|
items.push({ type: "recent", item: pkg })
|
|
@@ -225,7 +290,13 @@ const CmdKMenu = () => {
|
|
|
225
290
|
})
|
|
226
291
|
|
|
227
292
|
return items
|
|
228
|
-
}, [
|
|
293
|
+
}, [
|
|
294
|
+
searchQuery,
|
|
295
|
+
searchResults,
|
|
296
|
+
accountSearchResults,
|
|
297
|
+
recentPackages,
|
|
298
|
+
filteredStaticOptions,
|
|
299
|
+
])
|
|
229
300
|
|
|
230
301
|
useHotkeyCombo("cmd+k", () => {
|
|
231
302
|
setOpen((prev) => !prev)
|
|
@@ -285,6 +356,10 @@ const CmdKMenu = () => {
|
|
|
285
356
|
window.location.href = `/${item.owner_github_username}/${item.unscoped_name}`
|
|
286
357
|
setOpen(false)
|
|
287
358
|
break
|
|
359
|
+
case "account":
|
|
360
|
+
window.location.href = `/${item.github_username}`
|
|
361
|
+
setOpen(false)
|
|
362
|
+
break
|
|
288
363
|
case "blank":
|
|
289
364
|
case "template":
|
|
290
365
|
if (!item.disabled) {
|
|
@@ -362,7 +437,11 @@ const CmdKMenu = () => {
|
|
|
362
437
|
)}
|
|
363
438
|
</div>
|
|
364
439
|
</div>
|
|
365
|
-
<div className="flex items-center gap-
|
|
440
|
+
<div className="flex items-center gap-2 flex-shrink-0">
|
|
441
|
+
<div className="flex items-center gap-1 text-gray-500">
|
|
442
|
+
<Star className="w-3 h-3 fill-yellow-400 text-yellow-400" />
|
|
443
|
+
<span className="text-xs">{data.star_count ?? 0}</span>
|
|
444
|
+
</div>
|
|
366
445
|
<span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded">
|
|
367
446
|
package
|
|
368
447
|
</span>
|
|
@@ -371,6 +450,41 @@ const CmdKMenu = () => {
|
|
|
371
450
|
</div>
|
|
372
451
|
)
|
|
373
452
|
|
|
453
|
+
case "account":
|
|
454
|
+
return (
|
|
455
|
+
<div
|
|
456
|
+
key={`account-${data.account_id}`}
|
|
457
|
+
ref={isSelected ? selectedItemRef : null}
|
|
458
|
+
className={baseClasses}
|
|
459
|
+
onClick={() => !disabled && handleItemSelect(item)}
|
|
460
|
+
>
|
|
461
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
462
|
+
<img
|
|
463
|
+
src={`https://github.com/${data.github_username}.png`}
|
|
464
|
+
alt={`${data.github_username} avatar`}
|
|
465
|
+
className="w-6 h-6 rounded-full flex-shrink-0"
|
|
466
|
+
onError={(e) => {
|
|
467
|
+
const target = e.target as HTMLImageElement
|
|
468
|
+
target.style.display = "none"
|
|
469
|
+
target.nextElementSibling?.classList.remove("hidden")
|
|
470
|
+
}}
|
|
471
|
+
/>
|
|
472
|
+
<User className="w-6 h-6 text-gray-400 flex-shrink-0 hidden" />
|
|
473
|
+
<div className="flex flex-col min-w-0">
|
|
474
|
+
<span className="font-medium text-gray-900 truncate">
|
|
475
|
+
{renderHighlighted(data, data.github_username)}
|
|
476
|
+
</span>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
<div className="flex items-center gap-1 flex-shrink-0">
|
|
480
|
+
<span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded">
|
|
481
|
+
user
|
|
482
|
+
</span>
|
|
483
|
+
{isSelected && <ArrowRight className="w-3 h-3 text-gray-400" />}
|
|
484
|
+
</div>
|
|
485
|
+
</div>
|
|
486
|
+
)
|
|
487
|
+
|
|
374
488
|
case "blank":
|
|
375
489
|
case "template":
|
|
376
490
|
return (
|
|
@@ -464,7 +578,7 @@ const CmdKMenu = () => {
|
|
|
464
578
|
</div>
|
|
465
579
|
|
|
466
580
|
<Command.List className="max-h-80 overflow-y-auto p-2 space-y-4">
|
|
467
|
-
{isSearching ? (
|
|
581
|
+
{isSearching || isSearchingAccounts ? (
|
|
468
582
|
<Command.Loading className="p-6 text-center text-gray-500">
|
|
469
583
|
<div className="flex items-center justify-center gap-2">
|
|
470
584
|
<div className="w-3 h-3 border-2 border-gray-300 border-t-gray-600 rounded-full animate-spin"></div>
|
|
@@ -476,7 +590,7 @@ const CmdKMenu = () => {
|
|
|
476
590
|
{searchQuery && searchResults.length > 0 && (
|
|
477
591
|
<div>
|
|
478
592
|
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
|
|
479
|
-
|
|
593
|
+
Packages
|
|
480
594
|
</h3>
|
|
481
595
|
<div className="space-y-1">
|
|
482
596
|
{searchResults.slice(0, 8).map((pkg, localIndex) => {
|
|
@@ -490,6 +604,25 @@ const CmdKMenu = () => {
|
|
|
490
604
|
</div>
|
|
491
605
|
)}
|
|
492
606
|
|
|
607
|
+
{searchQuery && accountSearchResults.length > 0 && (
|
|
608
|
+
<div>
|
|
609
|
+
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
|
|
610
|
+
Users
|
|
611
|
+
</h3>
|
|
612
|
+
<div className="space-y-1">
|
|
613
|
+
{accountSearchResults
|
|
614
|
+
.slice(0, 5)
|
|
615
|
+
.map((account, localIndex) => {
|
|
616
|
+
const globalIndex = searchResults.length + localIndex
|
|
617
|
+
return renderItem(
|
|
618
|
+
{ type: "account", item: account },
|
|
619
|
+
globalIndex,
|
|
620
|
+
)
|
|
621
|
+
})}
|
|
622
|
+
</div>
|
|
623
|
+
</div>
|
|
624
|
+
)}
|
|
625
|
+
|
|
493
626
|
{!searchQuery && recentPackages.length > 0 && (
|
|
494
627
|
<div>
|
|
495
628
|
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2 flex items-center gap-1">
|
|
@@ -518,7 +651,7 @@ const CmdKMenu = () => {
|
|
|
518
651
|
(template, localIndex) => {
|
|
519
652
|
const globalIndex =
|
|
520
653
|
(searchQuery
|
|
521
|
-
? searchResults.length
|
|
654
|
+
? searchResults.length + accountSearchResults.length
|
|
522
655
|
: recentPackages.length) + localIndex
|
|
523
656
|
return renderItem(
|
|
524
657
|
{
|
|
@@ -544,7 +677,7 @@ const CmdKMenu = () => {
|
|
|
544
677
|
(template, localIndex) => {
|
|
545
678
|
const globalIndex =
|
|
546
679
|
(searchQuery
|
|
547
|
-
? searchResults.length
|
|
680
|
+
? searchResults.length + accountSearchResults.length
|
|
548
681
|
: recentPackages.length) +
|
|
549
682
|
filteredStaticOptions.blankTemplates.length +
|
|
550
683
|
localIndex
|
|
@@ -568,7 +701,7 @@ const CmdKMenu = () => {
|
|
|
568
701
|
(option, localIndex) => {
|
|
569
702
|
const globalIndex =
|
|
570
703
|
(searchQuery
|
|
571
|
-
? searchResults.length
|
|
704
|
+
? searchResults.length + accountSearchResults.length
|
|
572
705
|
: recentPackages.length) +
|
|
573
706
|
filteredStaticOptions.blankTemplates.length +
|
|
574
707
|
filteredStaticOptions.templates.length +
|
|
@@ -585,10 +718,12 @@ const CmdKMenu = () => {
|
|
|
585
718
|
|
|
586
719
|
{searchQuery &&
|
|
587
720
|
!searchResults.length &&
|
|
721
|
+
!accountSearchResults.length &&
|
|
588
722
|
!filteredStaticOptions.blankTemplates.length &&
|
|
589
723
|
!filteredStaticOptions.templates.length &&
|
|
590
724
|
!filteredStaticOptions.importOptions.length &&
|
|
591
|
-
!isSearching &&
|
|
725
|
+
!isSearching &&
|
|
726
|
+
!isSearchingAccounts && (
|
|
592
727
|
<Command.Empty className="py-8 text-center">
|
|
593
728
|
<div className="text-gray-400 mb-1">No results found</div>
|
|
594
729
|
<div className="text-gray-500 text-xs">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Button } from "@/components/ui/button"
|
|
2
|
-
import { CircuitBoard, Search } from "lucide-react"
|
|
2
|
+
import { CircuitBoard, Search, Menu, X } from "lucide-react"
|
|
3
3
|
import { Link } from "wouter"
|
|
4
4
|
import { PrefetchPageLink } from "./PrefetchPageLink"
|
|
5
5
|
import { HeaderLogin } from "./HeaderLogin"
|
|
@@ -53,10 +53,12 @@ const SearchButtonComponent = () => {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export const Header2 = () => {
|
|
56
|
+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
56
57
|
const isLoggedIn = useGlobalStore((state) => Boolean(state.session))
|
|
58
|
+
|
|
57
59
|
return (
|
|
58
60
|
<>
|
|
59
|
-
<header className="sticky top-0 z-50 w-full border-b bg-
|
|
61
|
+
<header className="sticky top-0 z-50 w-full border-b bg-white md:bg-white/80 md:backdrop-blur supports-[backdrop-filter]:md:bg-white/60">
|
|
60
62
|
<div className="container mx-auto flex h-16 items-center justify-between px-2 md:px-6">
|
|
61
63
|
<PrefetchPageLink
|
|
62
64
|
href="/"
|
|
@@ -65,23 +67,17 @@ export const Header2 = () => {
|
|
|
65
67
|
<CircuitBoard className="h-6 w-6" />
|
|
66
68
|
<span className="text-lg font-bold">tscircuit</span>
|
|
67
69
|
</PrefetchPageLink>
|
|
68
|
-
|
|
70
|
+
|
|
71
|
+
{/* Desktop Navigation */}
|
|
72
|
+
<nav className="hidden md:flex gap-6">
|
|
69
73
|
{isLoggedIn && (
|
|
70
|
-
<
|
|
74
|
+
<PrefetchPageLink
|
|
71
75
|
className="text-sm font-medium hover:underline underline-offset-4"
|
|
72
76
|
href="/dashboard"
|
|
73
77
|
>
|
|
74
78
|
Dashboard
|
|
75
|
-
</
|
|
79
|
+
</PrefetchPageLink>
|
|
76
80
|
)}
|
|
77
|
-
</nav>
|
|
78
|
-
<nav className="hidden md:flex gap-6">
|
|
79
|
-
<PrefetchPageLink
|
|
80
|
-
className="text-sm font-medium hover:underline underline-offset-4"
|
|
81
|
-
href="/dashboard"
|
|
82
|
-
>
|
|
83
|
-
Dashboard
|
|
84
|
-
</PrefetchPageLink>
|
|
85
81
|
<PrefetchPageLink
|
|
86
82
|
className="text-sm font-medium hover:underline underline-offset-4"
|
|
87
83
|
href="/quickstart"
|
|
@@ -94,12 +90,6 @@ export const Header2 = () => {
|
|
|
94
90
|
>
|
|
95
91
|
Datasheets
|
|
96
92
|
</PrefetchPageLink>
|
|
97
|
-
{/* <a
|
|
98
|
-
className="text-sm font-medium hover:underline underline-offset-4"
|
|
99
|
-
href="https://github.com/tscircuit/tscircuit"
|
|
100
|
-
>
|
|
101
|
-
Github
|
|
102
|
-
</a> */}
|
|
103
93
|
<a
|
|
104
94
|
className="text-sm font-medium hover:underline underline-offset-4"
|
|
105
95
|
href="https://docs.tscircuit.com"
|
|
@@ -119,16 +109,107 @@ export const Header2 = () => {
|
|
|
119
109
|
Contact
|
|
120
110
|
</a>
|
|
121
111
|
</nav>
|
|
122
|
-
|
|
112
|
+
|
|
113
|
+
{/* Desktop Right Side */}
|
|
114
|
+
<div className="hidden md:flex items-center gap-4">
|
|
123
115
|
<SearchButtonComponent />
|
|
124
|
-
{isLoggedIn &&
|
|
125
|
-
<div className="hidden sm:block">
|
|
126
|
-
<HeaderDropdown />
|
|
127
|
-
</div>
|
|
128
|
-
)}
|
|
116
|
+
{isLoggedIn && <HeaderDropdown />}
|
|
129
117
|
<HeaderLogin />
|
|
130
118
|
</div>
|
|
119
|
+
|
|
120
|
+
{/* Mobile Right Side */}
|
|
121
|
+
<div className="flex md:hidden items-center gap-2">
|
|
122
|
+
<Button
|
|
123
|
+
variant="ghost"
|
|
124
|
+
size="icon"
|
|
125
|
+
onClick={() => (window.location.href = "/search")}
|
|
126
|
+
className="h-8 w-8"
|
|
127
|
+
aria-label="Go to search"
|
|
128
|
+
>
|
|
129
|
+
<Search className="size-4" />
|
|
130
|
+
</Button>
|
|
131
|
+
{isLoggedIn ? (
|
|
132
|
+
<HeaderLogin />
|
|
133
|
+
) : (
|
|
134
|
+
<PrefetchPageLink href="/quickstart">
|
|
135
|
+
<Button size="sm" className="text-xs px-3 py-1">
|
|
136
|
+
Get Started
|
|
137
|
+
</Button>
|
|
138
|
+
</PrefetchPageLink>
|
|
139
|
+
)}
|
|
140
|
+
<button
|
|
141
|
+
className="p-2"
|
|
142
|
+
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
143
|
+
aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
|
|
144
|
+
>
|
|
145
|
+
{mobileMenuOpen ? <X size={20} /> : <Menu size={20} />}
|
|
146
|
+
</button>
|
|
147
|
+
</div>
|
|
131
148
|
</div>
|
|
149
|
+
|
|
150
|
+
{/* Mobile Menu */}
|
|
151
|
+
{mobileMenuOpen && (
|
|
152
|
+
<div className="md:hidden border-t bg-white ">
|
|
153
|
+
<div className="container mx-auto px-4 py-3">
|
|
154
|
+
<nav className="mb-4">
|
|
155
|
+
<div className="flex flex-col items-center gap-1">
|
|
156
|
+
{isLoggedIn && (
|
|
157
|
+
<PrefetchPageLink
|
|
158
|
+
className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
|
|
159
|
+
href="/dashboard"
|
|
160
|
+
onClick={() => setMobileMenuOpen(false)}
|
|
161
|
+
>
|
|
162
|
+
Dashboard
|
|
163
|
+
</PrefetchPageLink>
|
|
164
|
+
)}
|
|
165
|
+
<PrefetchPageLink
|
|
166
|
+
className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
|
|
167
|
+
href="/quickstart"
|
|
168
|
+
onClick={() => setMobileMenuOpen(false)}
|
|
169
|
+
>
|
|
170
|
+
Editor
|
|
171
|
+
</PrefetchPageLink>
|
|
172
|
+
<PrefetchPageLink
|
|
173
|
+
className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
|
|
174
|
+
href="/datasheets"
|
|
175
|
+
onClick={() => setMobileMenuOpen(false)}
|
|
176
|
+
>
|
|
177
|
+
Datasheets
|
|
178
|
+
</PrefetchPageLink>
|
|
179
|
+
<a
|
|
180
|
+
className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
|
|
181
|
+
href="https://docs.tscircuit.com"
|
|
182
|
+
>
|
|
183
|
+
Docs
|
|
184
|
+
</a>
|
|
185
|
+
<a
|
|
186
|
+
className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
|
|
187
|
+
href="https://tscircuit.com/join"
|
|
188
|
+
>
|
|
189
|
+
Discord
|
|
190
|
+
</a>
|
|
191
|
+
<a
|
|
192
|
+
className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
|
|
193
|
+
href="mailto:hello@tscircuit.com"
|
|
194
|
+
>
|
|
195
|
+
Contact
|
|
196
|
+
</a>
|
|
197
|
+
</div>
|
|
198
|
+
</nav>
|
|
199
|
+
<div className="flex flex-col items-center gap-4 pt-4 border-t border-gray-200">
|
|
200
|
+
{isLoggedIn ? (
|
|
201
|
+
<div className="w-full flex justify-center">
|
|
202
|
+
<HeaderDropdown />
|
|
203
|
+
</div>
|
|
204
|
+
) : (
|
|
205
|
+
<div className="w-full flex justify-center">
|
|
206
|
+
<HeaderLogin />
|
|
207
|
+
</div>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
132
213
|
</header>
|
|
133
214
|
<CmdKMenu />
|
|
134
215
|
<Analytics />
|