@tscircuit/fake-snippets 0.0.44 → 0.0.46
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/.github/workflows/bun-pver-release.yml +1 -0
- package/bun.lock +43 -11
- package/dist/bundle.js +405 -335
- package/dist/schema.d.ts +1845 -0
- package/dist/schema.js +251 -0
- package/fake-snippets-api/routes/api/_fake/received_quotes.ts +66 -0
- package/fake-snippets-api/routes/api/package_releases/update.ts +25 -18
- package/package.json +8 -3
- package/src/components/CodeAndPreview.tsx +0 -1
- package/src/components/CodeEditor.tsx +0 -1
- package/src/components/CodeEditorHeader.tsx +0 -25
- package/src/components/EditorNav.tsx +10 -8
- package/src/components/ErrorOutline.tsx +35 -0
- package/src/components/FileSidebar.tsx +46 -16
- package/src/components/NotFound.tsx +37 -0
- package/src/components/PreviewContent.tsx +0 -6
- package/src/components/TrendingSnippetCarousel.tsx +1 -1
- package/src/components/ViewPackagePage/components/package-header.tsx +24 -3
- package/src/components/ViewPackagePage/utils/is-hidden-file.ts +0 -1
- package/src/components/dialogs/package-visibility-settings-dialog.tsx +10 -1
- package/src/components/dialogs/view-ts-files-dialog.tsx +0 -6
- package/src/components/package-port/CodeAndPreview.tsx +24 -9
- package/src/components/package-port/CodeEditor.tsx +26 -38
- package/src/components/package-port/CodeEditorHeader.tsx +117 -39
- package/src/components/package-port/EditorNav.tsx +10 -8
- package/src/components/ui/tree-view.tsx +5 -1
- package/src/{prettier.ts → lib/types.ts} +3 -1
- package/src/lib/utils/findTargetFile.ts +62 -0
- package/src/lib/utils/load-prettier.ts +3 -0
- package/src/pages/404.tsx +2 -33
- package/src/pages/package-editor.tsx +14 -3
- package/src/pages/user-profile.tsx +66 -27
- package/src/components/FootprintDialog.tsx +0 -339
- package/src/components/ParametersEditor.tsx +0 -140
- package/src/lib/utils/parseFootprintParams.ts +0 -52
package/dist/schema.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// fake-snippets-api/lib/db/schema.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var errorSchema = z.object({
|
|
4
|
+
error_code: z.string(),
|
|
5
|
+
message: z.string()
|
|
6
|
+
}).passthrough();
|
|
7
|
+
var errorResponseSchema = z.object({
|
|
8
|
+
error: errorSchema
|
|
9
|
+
});
|
|
10
|
+
var snippetSchema = z.object({
|
|
11
|
+
snippet_id: z.string(),
|
|
12
|
+
package_release_id: z.string(),
|
|
13
|
+
name: z.string(),
|
|
14
|
+
unscoped_name: z.string(),
|
|
15
|
+
owner_name: z.string(),
|
|
16
|
+
is_starred: z.boolean().default(false),
|
|
17
|
+
code: z.string(),
|
|
18
|
+
dts: z.string().optional(),
|
|
19
|
+
compiled_js: z.string().optional().nullable(),
|
|
20
|
+
circuit_json: z.array(z.record(z.any())).optional().nullable(),
|
|
21
|
+
manual_edits_json_content: z.string().optional().nullable(),
|
|
22
|
+
created_at: z.string(),
|
|
23
|
+
updated_at: z.string(),
|
|
24
|
+
snippet_type: z.enum(["board", "package", "model", "footprint"]),
|
|
25
|
+
description: z.string().optional(),
|
|
26
|
+
version: z.string().default("0.0.1"),
|
|
27
|
+
star_count: z.number().default(0),
|
|
28
|
+
is_private: z.boolean().default(false),
|
|
29
|
+
is_public: z.boolean().default(true),
|
|
30
|
+
is_unlisted: z.boolean().default(false)
|
|
31
|
+
});
|
|
32
|
+
var sessionSchema = z.object({
|
|
33
|
+
session_id: z.string(),
|
|
34
|
+
account_id: z.string(),
|
|
35
|
+
expires_at: z.string(),
|
|
36
|
+
is_cli_session: z.boolean()
|
|
37
|
+
});
|
|
38
|
+
var loginPageSchema = z.object({
|
|
39
|
+
login_page_id: z.string(),
|
|
40
|
+
login_page_auth_token: z.string(),
|
|
41
|
+
was_login_successful: z.boolean(),
|
|
42
|
+
has_been_used_to_create_session: z.boolean(),
|
|
43
|
+
created_at: z.string(),
|
|
44
|
+
expires_at: z.string()
|
|
45
|
+
});
|
|
46
|
+
var shippingInfoSchema = z.object({
|
|
47
|
+
firstName: z.string(),
|
|
48
|
+
lastName: z.string(),
|
|
49
|
+
companyName: z.string().optional(),
|
|
50
|
+
address: z.string(),
|
|
51
|
+
apartment: z.string().optional(),
|
|
52
|
+
city: z.string(),
|
|
53
|
+
state: z.string(),
|
|
54
|
+
zipCode: z.string(),
|
|
55
|
+
country: z.string(),
|
|
56
|
+
phone: z.string()
|
|
57
|
+
});
|
|
58
|
+
var accountSchema = z.object({
|
|
59
|
+
account_id: z.string(),
|
|
60
|
+
github_username: z.string(),
|
|
61
|
+
shippingInfo: shippingInfoSchema.optional()
|
|
62
|
+
});
|
|
63
|
+
var orderSchema = z.object({
|
|
64
|
+
order_id: z.string(),
|
|
65
|
+
account_id: z.string().nullable(),
|
|
66
|
+
is_running: z.boolean(),
|
|
67
|
+
is_started: z.boolean(),
|
|
68
|
+
is_finished: z.boolean(),
|
|
69
|
+
error: errorSchema.nullable(),
|
|
70
|
+
has_error: z.boolean(),
|
|
71
|
+
created_at: z.string(),
|
|
72
|
+
started_at: z.string().nullable(),
|
|
73
|
+
completed_at: z.string().nullable(),
|
|
74
|
+
circuit_json: z.any()
|
|
75
|
+
});
|
|
76
|
+
var orderFileSchema = z.object({
|
|
77
|
+
order_file_id: z.string(),
|
|
78
|
+
order_id: z.string(),
|
|
79
|
+
is_gerbers_zip: z.boolean(),
|
|
80
|
+
content_type: z.string(),
|
|
81
|
+
for_provider: z.string().nullable(),
|
|
82
|
+
uploaded_at: z.string(),
|
|
83
|
+
content_text: z.string().nullable(),
|
|
84
|
+
content_bytes: z.instanceof(Uint8Array).nullable()
|
|
85
|
+
});
|
|
86
|
+
var shippingOptionSchema = z.object({
|
|
87
|
+
carrier: z.string(),
|
|
88
|
+
service: z.string(),
|
|
89
|
+
cost: z.number()
|
|
90
|
+
});
|
|
91
|
+
var quotedComponentSchema = z.object({
|
|
92
|
+
manufacturer_part_number: z.string().nullable(),
|
|
93
|
+
supplier_part_number: z.string().nullable(),
|
|
94
|
+
quantity: z.number().default(0),
|
|
95
|
+
unit_price: z.number().default(0),
|
|
96
|
+
total_price: z.number().default(0),
|
|
97
|
+
available: z.boolean().default(true)
|
|
98
|
+
});
|
|
99
|
+
var orderQuoteSchema = z.object({
|
|
100
|
+
order_quote_id: z.string(),
|
|
101
|
+
account_id: z.string().nullable(),
|
|
102
|
+
package_release_id: z.string().nullable(),
|
|
103
|
+
is_completed: z.boolean().default(false),
|
|
104
|
+
is_processing: z.boolean().default(true),
|
|
105
|
+
vendor_name: z.string(),
|
|
106
|
+
error: errorSchema.nullable(),
|
|
107
|
+
has_error: z.boolean().default(false),
|
|
108
|
+
created_at: z.string(),
|
|
109
|
+
updated_at: z.string(),
|
|
110
|
+
completed_at: z.string().nullable(),
|
|
111
|
+
quoted_components: z.array(quotedComponentSchema).nullable(),
|
|
112
|
+
bare_pcb_cost: z.number().default(0),
|
|
113
|
+
shipping_options: z.array(shippingOptionSchema),
|
|
114
|
+
total_cost: z.number().default(0)
|
|
115
|
+
});
|
|
116
|
+
var accountSnippetSchema = z.object({
|
|
117
|
+
account_id: z.string(),
|
|
118
|
+
snippet_id: z.string(),
|
|
119
|
+
has_starred: z.boolean(),
|
|
120
|
+
created_at: z.string(),
|
|
121
|
+
updated_at: z.string()
|
|
122
|
+
});
|
|
123
|
+
var accountPackageSchema = z.object({
|
|
124
|
+
account_package_id: z.string(),
|
|
125
|
+
account_id: z.string(),
|
|
126
|
+
package_id: z.string(),
|
|
127
|
+
is_starred: z.boolean(),
|
|
128
|
+
created_at: z.string(),
|
|
129
|
+
updated_at: z.string()
|
|
130
|
+
});
|
|
131
|
+
var packageReleaseSchema = z.object({
|
|
132
|
+
package_release_id: z.string(),
|
|
133
|
+
package_id: z.string(),
|
|
134
|
+
version: z.string().nullable(),
|
|
135
|
+
is_locked: z.boolean(),
|
|
136
|
+
is_latest: z.boolean(),
|
|
137
|
+
created_at: z.string().datetime(),
|
|
138
|
+
commit_sha: z.string().nullable().optional(),
|
|
139
|
+
license: z.string().nullable().optional()
|
|
140
|
+
});
|
|
141
|
+
var packageFileSchema = z.object({
|
|
142
|
+
package_file_id: z.string(),
|
|
143
|
+
package_release_id: z.string(),
|
|
144
|
+
file_path: z.string(),
|
|
145
|
+
content_text: z.string().nullable().optional(),
|
|
146
|
+
created_at: z.string().datetime(),
|
|
147
|
+
content_mimetype: z.string().nullable().optional(),
|
|
148
|
+
is_release_tarball: z.boolean().optional(),
|
|
149
|
+
npm_pack_output: z.any().nullable().optional()
|
|
150
|
+
});
|
|
151
|
+
var packageSchema = z.object({
|
|
152
|
+
package_id: z.string(),
|
|
153
|
+
creator_account_id: z.string(),
|
|
154
|
+
owner_org_id: z.string(),
|
|
155
|
+
owner_github_username: z.string().nullable(),
|
|
156
|
+
name: z.string(),
|
|
157
|
+
unscoped_name: z.string(),
|
|
158
|
+
description: z.string().nullable(),
|
|
159
|
+
created_at: z.string().datetime(),
|
|
160
|
+
updated_at: z.string().datetime(),
|
|
161
|
+
is_snippet: z.boolean().default(false),
|
|
162
|
+
is_board: z.boolean().default(false),
|
|
163
|
+
is_package: z.boolean().default(false),
|
|
164
|
+
is_model: z.boolean().default(false),
|
|
165
|
+
is_footprint: z.boolean().default(false),
|
|
166
|
+
is_private: z.boolean().nullable().default(false),
|
|
167
|
+
is_public: z.boolean().nullable().default(true),
|
|
168
|
+
is_unlisted: z.boolean().nullable().default(false),
|
|
169
|
+
is_source_from_github: z.boolean().default(false),
|
|
170
|
+
snippet_type: z.enum(["board", "package", "model", "footprint"]).optional(),
|
|
171
|
+
latest_package_release_id: z.string().nullable(),
|
|
172
|
+
latest_version: z.string().nullable(),
|
|
173
|
+
license: z.string().nullable(),
|
|
174
|
+
website: z.string().nullable().default(null),
|
|
175
|
+
star_count: z.number().default(0),
|
|
176
|
+
ai_description: z.string().nullable(),
|
|
177
|
+
latest_license: z.string().nullable().optional(),
|
|
178
|
+
ai_usage_instructions: z.string().nullable()
|
|
179
|
+
});
|
|
180
|
+
var jlcpcbOrderStateSchema = z.object({
|
|
181
|
+
jlcpcb_order_state_id: z.string(),
|
|
182
|
+
order_id: z.string(),
|
|
183
|
+
are_gerbers_uploaded: z.boolean().default(false),
|
|
184
|
+
is_gerber_analyzed: z.boolean().default(false),
|
|
185
|
+
are_initial_costs_calculated: z.boolean().default(false),
|
|
186
|
+
is_pcb_added_to_cart: z.boolean().default(false),
|
|
187
|
+
is_bom_uploaded: z.boolean().default(false),
|
|
188
|
+
is_pnp_uploaded: z.boolean().default(false),
|
|
189
|
+
is_bom_pnp_analyzed: z.boolean().default(false),
|
|
190
|
+
is_bom_parsing_complete: z.boolean().default(false),
|
|
191
|
+
are_components_available: z.boolean().default(false),
|
|
192
|
+
is_patch_map_generated: z.boolean().default(false),
|
|
193
|
+
is_json_merge_file_created: z.boolean().default(false),
|
|
194
|
+
is_dfm_result_generated: z.boolean().default(false),
|
|
195
|
+
are_files_downloaded: z.boolean().default(false),
|
|
196
|
+
are_product_categories_fetched: z.boolean().default(false),
|
|
197
|
+
are_final_costs_calculated: z.boolean().default(false),
|
|
198
|
+
is_json_merge_file_updated: z.boolean().default(false),
|
|
199
|
+
is_added_to_cart: z.boolean().default(false),
|
|
200
|
+
uploaded_gerber_metadata: z.any().nullable().default(null),
|
|
201
|
+
gerber_analysis: z.any().nullable().default(null),
|
|
202
|
+
created_at: z.string(),
|
|
203
|
+
are_gerbers_generated: z.boolean().default(false),
|
|
204
|
+
current_step: z.string().nullable().default(null)
|
|
205
|
+
});
|
|
206
|
+
var jlcpcbOrderStepRunSchema = z.object({
|
|
207
|
+
jlcpcb_order_step_run_id: z.string(),
|
|
208
|
+
is_running: z.boolean().nullable().default(null),
|
|
209
|
+
step_function_name: z.string().nullable().default(null),
|
|
210
|
+
jlcpcb_order_state_id: z.string().nullable().default(null),
|
|
211
|
+
error_message: z.string().nullable().default(null),
|
|
212
|
+
created_at: z.string()
|
|
213
|
+
});
|
|
214
|
+
var databaseSchema = z.object({
|
|
215
|
+
idCounter: z.number().default(0),
|
|
216
|
+
snippets: z.array(snippetSchema).default([]),
|
|
217
|
+
packageReleases: z.array(packageReleaseSchema).default([]),
|
|
218
|
+
packageFiles: z.array(packageFileSchema).default([]),
|
|
219
|
+
sessions: z.array(sessionSchema).default([]),
|
|
220
|
+
loginPages: z.array(loginPageSchema).default([]),
|
|
221
|
+
accounts: z.array(accountSchema).default([]),
|
|
222
|
+
packages: z.array(packageSchema).default([]),
|
|
223
|
+
orders: z.array(orderSchema).default([]),
|
|
224
|
+
orderFiles: z.array(orderFileSchema).default([]),
|
|
225
|
+
accountSnippets: z.array(accountSnippetSchema).default([]),
|
|
226
|
+
accountPackages: z.array(accountPackageSchema).default([]),
|
|
227
|
+
jlcpcbOrderState: z.array(jlcpcbOrderStateSchema).default([]),
|
|
228
|
+
jlcpcbOrderStepRuns: z.array(jlcpcbOrderStepRunSchema).default([]),
|
|
229
|
+
orderQuotes: z.array(orderQuoteSchema).default([])
|
|
230
|
+
});
|
|
231
|
+
export {
|
|
232
|
+
accountPackageSchema,
|
|
233
|
+
accountSchema,
|
|
234
|
+
accountSnippetSchema,
|
|
235
|
+
databaseSchema,
|
|
236
|
+
errorResponseSchema,
|
|
237
|
+
errorSchema,
|
|
238
|
+
jlcpcbOrderStateSchema,
|
|
239
|
+
jlcpcbOrderStepRunSchema,
|
|
240
|
+
loginPageSchema,
|
|
241
|
+
orderFileSchema,
|
|
242
|
+
orderQuoteSchema,
|
|
243
|
+
orderSchema,
|
|
244
|
+
packageFileSchema,
|
|
245
|
+
packageReleaseSchema,
|
|
246
|
+
packageSchema,
|
|
247
|
+
quotedComponentSchema,
|
|
248
|
+
sessionSchema,
|
|
249
|
+
shippingInfoSchema,
|
|
250
|
+
snippetSchema
|
|
251
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { orderQuoteSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
|
|
5
|
+
export default withRouteSpec({
|
|
6
|
+
methods: ["POST"],
|
|
7
|
+
auth: "session",
|
|
8
|
+
jsonBody: z.object({
|
|
9
|
+
order_quote_id: z.string(),
|
|
10
|
+
}),
|
|
11
|
+
jsonResponse: z.object({
|
|
12
|
+
order_quote: orderQuoteSchema.optional(),
|
|
13
|
+
error: z.string().optional(),
|
|
14
|
+
}),
|
|
15
|
+
})(async (req, ctx) => {
|
|
16
|
+
const { order_quote_id } = req.jsonBody
|
|
17
|
+
|
|
18
|
+
return ctx.json({
|
|
19
|
+
order_quote: {
|
|
20
|
+
order_quote_id,
|
|
21
|
+
account_id: "123",
|
|
22
|
+
package_release_id: "123",
|
|
23
|
+
is_completed: true,
|
|
24
|
+
is_processing: false,
|
|
25
|
+
vendor_name: "JLCPCB",
|
|
26
|
+
error: null,
|
|
27
|
+
has_error: false,
|
|
28
|
+
created_at: "2025-04-21",
|
|
29
|
+
updated_at: "2025-04-21",
|
|
30
|
+
completed_at: "2025-04-21",
|
|
31
|
+
quoted_components: [
|
|
32
|
+
{
|
|
33
|
+
manufacturer_part_number: "123",
|
|
34
|
+
supplier_part_number: "123",
|
|
35
|
+
quantity: 1,
|
|
36
|
+
unit_price: 100,
|
|
37
|
+
total_price: 100,
|
|
38
|
+
available: true,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
manufacturer_part_number: "1234",
|
|
42
|
+
supplier_part_number: "1234",
|
|
43
|
+
quantity: 1,
|
|
44
|
+
unit_price: 200,
|
|
45
|
+
total_price: 200,
|
|
46
|
+
available: true,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
bare_pcb_cost: 300,
|
|
50
|
+
shipping_options: [
|
|
51
|
+
{
|
|
52
|
+
carrier: "DHL",
|
|
53
|
+
service: "Express",
|
|
54
|
+
cost: 100,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
carrier: "FedEx",
|
|
58
|
+
service: "Express",
|
|
59
|
+
cost: 200,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
// TODO: shipping cost can vary so do the total cost calculation
|
|
63
|
+
total_cost: 100,
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
})
|
|
@@ -67,28 +67,35 @@ export default withRouteSpec({
|
|
|
67
67
|
})
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.filter(
|
|
74
|
-
(pr) =>
|
|
75
|
-
pr.package_id === release.package_id &&
|
|
76
|
-
pr.package_release_id !== releaseId &&
|
|
77
|
-
pr.is_latest,
|
|
78
|
-
)
|
|
79
|
-
.forEach((pr) => {
|
|
80
|
-
pr.is_latest = false
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Update the release
|
|
85
|
-
Object.assign(release, {
|
|
70
|
+
// Create updated release object
|
|
71
|
+
const updatedRelease = {
|
|
72
|
+
...release,
|
|
86
73
|
...(is_locked !== undefined && { is_locked }),
|
|
87
74
|
...(is_latest !== undefined && { is_latest }),
|
|
88
75
|
...(license !== undefined && { license }),
|
|
89
|
-
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Handle is_latest updates atomically
|
|
79
|
+
if (is_latest !== undefined && is_latest) {
|
|
80
|
+
// Get all releases for this package that are currently marked as latest
|
|
81
|
+
const otherLatestReleases = ctx.db.packageReleases.filter(
|
|
82
|
+
(pr) =>
|
|
83
|
+
pr.package_id === release.package_id &&
|
|
84
|
+
pr.package_release_id !== releaseId &&
|
|
85
|
+
pr.is_latest,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
// Update all releases in a single operation
|
|
89
|
+
for (const latestRelease of otherLatestReleases) {
|
|
90
|
+
ctx.db.updatePackageRelease({
|
|
91
|
+
...latestRelease,
|
|
92
|
+
is_latest: false,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
// Update the target release
|
|
98
|
+
ctx.db.updatePackageRelease(updatedRelease)
|
|
92
99
|
|
|
93
100
|
return ctx.json({
|
|
94
101
|
ok: true,
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/fake-snippets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.46",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/tscircuit/snippets"
|
|
8
8
|
},
|
|
9
9
|
"main": "dist/index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.js",
|
|
12
|
+
"./schema": "./dist/schema.js"
|
|
13
|
+
},
|
|
10
14
|
"scripts": {
|
|
11
15
|
"start": "bun run dev",
|
|
12
16
|
"snapshot": "bun scripts/snapshot.ts",
|
|
@@ -21,7 +25,8 @@
|
|
|
21
25
|
"lint": "biome format .",
|
|
22
26
|
"build:fake-api:tsup": "tsup-node ./fake-snippets-api/lib/index.ts --format esm --dts",
|
|
23
27
|
"build:fake-api:bundle": "winterspec bundle -o dist/bundle.js",
|
|
24
|
-
"build:fake-api": "bun run build:fake-api:tsup && bun run build:fake-api:bundle",
|
|
28
|
+
"build:fake-api": "bun run build:fake-api:tsup && bun run build:fake-api:bundle && bun run build:fake-api:schema",
|
|
29
|
+
"build:fake-api:schema": "tsup-node ./fake-snippets-api/lib/db/schema.ts --format esm --dts",
|
|
25
30
|
"generate-images": "bun run scripts/generate-image-sizes.ts",
|
|
26
31
|
"generate-sitemap": "bun run scripts/generate-sitemap.ts"
|
|
27
32
|
},
|
|
@@ -136,7 +141,7 @@
|
|
|
136
141
|
"@playwright/test": "^1.48.0",
|
|
137
142
|
"@tscircuit/core": "^0.0.370",
|
|
138
143
|
"@tscircuit/prompt-benchmarks": "^0.0.28",
|
|
139
|
-
"@tscircuit/runframe": "^0.0.
|
|
144
|
+
"@tscircuit/runframe": "^0.0.357",
|
|
140
145
|
"@types/babel__standalone": "^7.1.7",
|
|
141
146
|
"@types/bun": "^1.1.10",
|
|
142
147
|
"@types/country-list": "^2.1.4",
|
|
@@ -10,7 +10,6 @@ import { decodeUrlHashToText } from "@/lib/decodeUrlHashToText"
|
|
|
10
10
|
import { getSnippetTemplate } from "@/lib/get-snippet-template"
|
|
11
11
|
import { cn } from "@/lib/utils"
|
|
12
12
|
import { parseJsonOrNull } from "@/lib/utils/parseJsonOrNull"
|
|
13
|
-
import "@/prettier"
|
|
14
13
|
import type { Snippet } from "fake-snippets-api/lib/db/schema"
|
|
15
14
|
import { Loader2 } from "lucide-react"
|
|
16
15
|
import { useEffect, useMemo, useState } from "react"
|
|
@@ -8,8 +8,6 @@ import {
|
|
|
8
8
|
} from "@/components/ui/select"
|
|
9
9
|
import { useImportSnippetDialog } from "./dialogs/import-snippet-dialog"
|
|
10
10
|
import { useToast } from "@/hooks/use-toast"
|
|
11
|
-
import { FootprintDialog } from "./FootprintDialog"
|
|
12
|
-
import { useState } from "react"
|
|
13
11
|
import {
|
|
14
12
|
DropdownMenu,
|
|
15
13
|
DropdownMenuContent,
|
|
@@ -27,7 +25,6 @@ interface CodeEditorHeaderProps {
|
|
|
27
25
|
files: Record<FileName, string>
|
|
28
26
|
handleFileChange: (filename: FileName) => void
|
|
29
27
|
updateFileContent: (filename: FileName, content: string) => void
|
|
30
|
-
cursorPosition: number | null
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
export const CodeEditorHeader = ({
|
|
@@ -35,11 +32,9 @@ export const CodeEditorHeader = ({
|
|
|
35
32
|
files,
|
|
36
33
|
handleFileChange,
|
|
37
34
|
updateFileContent,
|
|
38
|
-
cursorPosition,
|
|
39
35
|
}: CodeEditorHeaderProps) => {
|
|
40
36
|
const { Dialog: ImportSnippetDialog, openDialog: openImportDialog } =
|
|
41
37
|
useImportSnippetDialog()
|
|
42
|
-
const [footprintDialogOpen, setFootprintDialogOpen] = useState(false)
|
|
43
38
|
const { toast } = useToast()
|
|
44
39
|
|
|
45
40
|
const formatCurrentFile = () => {
|
|
@@ -120,18 +115,6 @@ export const CodeEditorHeader = ({
|
|
|
120
115
|
</DropdownMenuContent>
|
|
121
116
|
</DropdownMenu>
|
|
122
117
|
)}
|
|
123
|
-
<DropdownMenu>
|
|
124
|
-
<DropdownMenuTrigger asChild>
|
|
125
|
-
<Button size="sm" variant="ghost">
|
|
126
|
-
Insert
|
|
127
|
-
</Button>
|
|
128
|
-
</DropdownMenuTrigger>
|
|
129
|
-
<DropdownMenuContent>
|
|
130
|
-
<DropdownMenuItem onClick={() => setFootprintDialogOpen(true)}>
|
|
131
|
-
Chip
|
|
132
|
-
</DropdownMenuItem>
|
|
133
|
-
</DropdownMenuContent>
|
|
134
|
-
</DropdownMenu>
|
|
135
118
|
<Button size="sm" variant="ghost" onClick={() => openImportDialog()}>
|
|
136
119
|
Import
|
|
137
120
|
</Button>
|
|
@@ -145,14 +128,6 @@ export const CodeEditorHeader = ({
|
|
|
145
128
|
updateFileContent(currentFile, newContent)
|
|
146
129
|
}}
|
|
147
130
|
/>
|
|
148
|
-
<FootprintDialog
|
|
149
|
-
currentFile={currentFile}
|
|
150
|
-
open={footprintDialogOpen}
|
|
151
|
-
onOpenChange={setFootprintDialogOpen}
|
|
152
|
-
updateFileContent={updateFileContent}
|
|
153
|
-
files={files}
|
|
154
|
-
cursorPosition={cursorPosition}
|
|
155
|
-
/>
|
|
156
131
|
</div>
|
|
157
132
|
)
|
|
158
133
|
}
|
|
@@ -195,14 +195,16 @@ export default function EditorNav({
|
|
|
195
195
|
{snippet && (
|
|
196
196
|
<>
|
|
197
197
|
<SnippetLink snippet={snippet} />
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
{snippet?.owner_name === session?.github_username && (
|
|
199
|
+
<Button
|
|
200
|
+
variant="ghost"
|
|
201
|
+
size="icon"
|
|
202
|
+
className="h-6 w-6 ml-2"
|
|
203
|
+
onClick={() => openRenameDialog()}
|
|
204
|
+
>
|
|
205
|
+
<Pencil className="h-3 w-3 text-gray-700" />
|
|
206
|
+
</Button>
|
|
207
|
+
)}
|
|
206
208
|
{isPrivate && (
|
|
207
209
|
<div className="relative group">
|
|
208
210
|
<LockClosedIcon className="h-3 w-3 text-gray-700" />
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AlertCircle } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
export function ErrorOutline({
|
|
4
|
+
error,
|
|
5
|
+
description,
|
|
6
|
+
}: { error?: Error; description?: string; children?: React.ReactNode }) {
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex items-center justify-center p-4">
|
|
9
|
+
<div className="max-w-lg w-full mx-auto p-6 sm:p-8 bg-red-100/80 rounded-xl shadow-lg border border-red-500">
|
|
10
|
+
<div className="flex flex-col items-center text-center">
|
|
11
|
+
<AlertCircle className="h-10 w-10 text-red-700 mb-4" />
|
|
12
|
+
<h2 className="text-xl sm:text-2xl font-bold text-red-700 mb-3">
|
|
13
|
+
Something strange happened
|
|
14
|
+
</h2>
|
|
15
|
+
<p className="text-red-600 font-medium mb-6 text-sm sm:text-base">
|
|
16
|
+
{description ||
|
|
17
|
+
"An unexpected error occurred while processing your request."}{" "}
|
|
18
|
+
<br />
|
|
19
|
+
Please try again or contact support if the problem persists.
|
|
20
|
+
</p>
|
|
21
|
+
{error?.message && (
|
|
22
|
+
<details className="w-full text-left bg-red-50 p-3 rounded-md border border-red-200">
|
|
23
|
+
<summary className="text-sm font-medium text-red-800 cursor-pointer hover:text-red-900">
|
|
24
|
+
Show Error Details
|
|
25
|
+
</summary>
|
|
26
|
+
<pre className="mt-2 text-xs text-red-700 bg-transparent p-0 rounded overflow-auto whitespace-pre-wrap break-words">
|
|
27
|
+
{error.message}
|
|
28
|
+
</pre>
|
|
29
|
+
</details>
|
|
30
|
+
)}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -2,6 +2,7 @@ import React, { useState } from "react"
|
|
|
2
2
|
import { cn } from "@/lib/utils"
|
|
3
3
|
import { File, Folder, PanelRightOpen } from "lucide-react"
|
|
4
4
|
import { TreeView, TreeDataItem } from "@/components/ui/tree-view"
|
|
5
|
+
import { isHiddenFile } from "./ViewPackagePage/utils/is-hidden-file"
|
|
5
6
|
|
|
6
7
|
type FileName = string
|
|
7
8
|
|
|
@@ -24,35 +25,63 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
|
24
25
|
const transformFilesToTreeData = (
|
|
25
26
|
files: Record<FileName, string>,
|
|
26
27
|
): TreeDataItem[] => {
|
|
27
|
-
|
|
28
|
+
type TreeNode = Omit<TreeDataItem, "children"> & {
|
|
29
|
+
children?: Record<string, TreeNode>
|
|
30
|
+
}
|
|
31
|
+
const root: Record<string, TreeNode> = {}
|
|
28
32
|
|
|
29
33
|
Object.keys(files).forEach((path) => {
|
|
30
|
-
const
|
|
34
|
+
const startsWithSlash = path.startsWith("/")
|
|
35
|
+
const parts = (startsWithSlash ? path.slice(1) : path).trim().split("/")
|
|
31
36
|
let current = root
|
|
32
37
|
|
|
33
|
-
let currentPath = ""
|
|
34
38
|
parts.forEach((part, index) => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
const isFile = index === parts.length - 1
|
|
40
|
+
const parentPath = parts.slice(0, index).join("/")
|
|
41
|
+
const currentPath = parentPath ? `${parentPath}/${part}` : part
|
|
42
|
+
const evaluatedFilePath = startsWithSlash
|
|
43
|
+
? `/${currentPath}`
|
|
44
|
+
: currentPath
|
|
45
|
+
if (
|
|
46
|
+
!current[part] &&
|
|
47
|
+
(!isHiddenFile(currentPath) ||
|
|
48
|
+
isHiddenFile(
|
|
49
|
+
currentFile.startsWith("/") ? currentFile.slice(1) : currentFile,
|
|
50
|
+
))
|
|
51
|
+
) {
|
|
40
52
|
current[part] = {
|
|
41
|
-
id:
|
|
42
|
-
name: part,
|
|
53
|
+
id: currentPath,
|
|
54
|
+
name: isFile ? part : part,
|
|
43
55
|
icon: isFile ? File : Folder,
|
|
44
|
-
onClick: isFile ? () => onFileSelect(
|
|
56
|
+
onClick: isFile ? () => onFileSelect(evaluatedFilePath) : undefined,
|
|
57
|
+
draggable: isFile,
|
|
58
|
+
droppable: !isFile,
|
|
59
|
+
children: isFile ? undefined : {},
|
|
45
60
|
}
|
|
46
61
|
}
|
|
47
|
-
|
|
62
|
+
|
|
63
|
+
if (!isFile && current[part].children) {
|
|
64
|
+
current = current[part].children
|
|
65
|
+
}
|
|
48
66
|
})
|
|
49
67
|
})
|
|
50
68
|
|
|
51
|
-
|
|
69
|
+
// Convert the nested object structure to array structure
|
|
70
|
+
const convertToArray = (
|
|
71
|
+
items: Record<string, TreeNode>,
|
|
72
|
+
): TreeDataItem[] => {
|
|
73
|
+
return Object.values(items).map((item) => ({
|
|
74
|
+
...item,
|
|
75
|
+
children: item.children ? convertToArray(item.children) : undefined,
|
|
76
|
+
}))
|
|
77
|
+
}
|
|
78
|
+
return convertToArray(root).filter((x) => {
|
|
79
|
+
if (x.children?.length === 0) return false
|
|
80
|
+
return true
|
|
81
|
+
})
|
|
52
82
|
}
|
|
53
83
|
|
|
54
84
|
const treeData = transformFilesToTreeData(files)
|
|
55
|
-
|
|
56
85
|
return (
|
|
57
86
|
<div
|
|
58
87
|
className={cn(
|
|
@@ -63,11 +92,12 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
|
63
92
|
>
|
|
64
93
|
<button
|
|
65
94
|
onClick={() => setSidebarOpen(!sidebarOpen)}
|
|
66
|
-
className={`z-[99] mt-2 ml-2 text-
|
|
95
|
+
className={`z-[99] mt-2 ml-2 text-gray-400 scale-90 transition-opacity duration-200 ${
|
|
96
|
+
!sidebarOpen ? "opacity-0 pointer-events-none" : "opacity-100"
|
|
97
|
+
}`}
|
|
67
98
|
>
|
|
68
99
|
<PanelRightOpen />
|
|
69
100
|
</button>
|
|
70
|
-
|
|
71
101
|
<TreeView
|
|
72
102
|
data={treeData}
|
|
73
103
|
initialSelectedItemId={currentFile}
|