pilothub 0.0.1 → 0.0.2

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.
Files changed (388) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +36 -129
  3. package/dist/browserAuth.d.ts +20 -0
  4. package/dist/browserAuth.js +156 -0
  5. package/dist/browserAuth.js.map +1 -0
  6. package/dist/browserAuth.test.d.ts +1 -0
  7. package/dist/browserAuth.test.js +83 -0
  8. package/dist/browserAuth.test.js.map +1 -0
  9. package/dist/cli/buildInfo.d.ts +3 -0
  10. package/dist/cli/buildInfo.js +103 -0
  11. package/dist/cli/buildInfo.js.map +1 -0
  12. package/dist/cli/commands/auth.d.ts +9 -0
  13. package/dist/cli/commands/auth.js +75 -0
  14. package/dist/cli/commands/auth.js.map +1 -0
  15. package/dist/cli/commands/delete.d.ts +11 -0
  16. package/dist/cli/commands/delete.js +67 -0
  17. package/dist/cli/commands/delete.js.map +1 -0
  18. package/dist/cli/commands/delete.test.d.ts +1 -0
  19. package/dist/cli/commands/delete.test.js +52 -0
  20. package/dist/cli/commands/delete.test.js.map +1 -0
  21. package/dist/cli/commands/publish.d.ts +9 -0
  22. package/dist/cli/commands/publish.js +87 -0
  23. package/dist/cli/commands/publish.js.map +1 -0
  24. package/dist/cli/commands/publish.test.d.ts +1 -0
  25. package/dist/cli/commands/publish.test.js +104 -0
  26. package/dist/cli/commands/publish.test.js.map +1 -0
  27. package/dist/cli/commands/skills.d.ts +23 -0
  28. package/dist/cli/commands/skills.js +298 -0
  29. package/dist/cli/commands/skills.js.map +1 -0
  30. package/dist/cli/commands/skills.test.d.ts +1 -0
  31. package/dist/cli/commands/skills.test.js +156 -0
  32. package/dist/cli/commands/skills.test.js.map +1 -0
  33. package/dist/cli/commands/star.d.ts +8 -0
  34. package/dist/cli/commands/star.js +38 -0
  35. package/dist/cli/commands/star.js.map +1 -0
  36. package/dist/cli/commands/sync.d.ts +3 -0
  37. package/dist/cli/commands/sync.js +160 -0
  38. package/dist/cli/commands/sync.js.map +1 -0
  39. package/dist/cli/commands/sync.test.d.ts +1 -0
  40. package/dist/cli/commands/sync.test.js +277 -0
  41. package/dist/cli/commands/sync.test.js.map +1 -0
  42. package/dist/cli/commands/syncHelpers.d.ts +76 -0
  43. package/dist/cli/commands/syncHelpers.js +349 -0
  44. package/dist/cli/commands/syncHelpers.js.map +1 -0
  45. package/dist/cli/commands/syncHelpers.test.d.ts +1 -0
  46. package/dist/cli/commands/syncHelpers.test.js +22 -0
  47. package/dist/cli/commands/syncHelpers.test.js.map +1 -0
  48. package/dist/cli/commands/syncTypes.d.ts +24 -0
  49. package/dist/cli/commands/syncTypes.js +2 -0
  50. package/dist/cli/commands/syncTypes.js.map +1 -0
  51. package/dist/cli/commands/unstar.d.ts +8 -0
  52. package/dist/cli/commands/unstar.js +38 -0
  53. package/dist/cli/commands/unstar.js.map +1 -0
  54. package/dist/cli/helpStyle.d.ts +13 -0
  55. package/dist/cli/helpStyle.js +38 -0
  56. package/dist/cli/helpStyle.js.map +1 -0
  57. package/dist/cli/pilotbotConfig.d.ts +6 -0
  58. package/dist/cli/pilotbotConfig.js +110 -0
  59. package/dist/cli/pilotbotConfig.js.map +1 -0
  60. package/dist/cli/pilotbotConfig.test.d.ts +1 -0
  61. package/dist/cli/pilotbotConfig.test.js +133 -0
  62. package/dist/cli/pilotbotConfig.test.js.map +1 -0
  63. package/dist/cli/registry.d.ts +7 -0
  64. package/dist/cli/registry.js +42 -0
  65. package/dist/cli/registry.js.map +1 -0
  66. package/dist/cli/registry.test.d.ts +1 -0
  67. package/dist/cli/registry.test.js +48 -0
  68. package/dist/cli/registry.test.js.map +1 -0
  69. package/dist/cli/scanSkills.d.ts +7 -0
  70. package/dist/cli/scanSkills.js +75 -0
  71. package/dist/cli/scanSkills.js.map +1 -0
  72. package/dist/cli/scanSkills.test.d.ts +1 -0
  73. package/dist/cli/scanSkills.test.js +60 -0
  74. package/dist/cli/scanSkills.test.js.map +1 -0
  75. package/dist/cli/slug.d.ts +2 -0
  76. package/dist/cli/slug.js +16 -0
  77. package/dist/cli/slug.js.map +1 -0
  78. package/dist/cli/types.d.ts +15 -0
  79. package/dist/cli/types.js +2 -0
  80. package/dist/cli/types.js.map +1 -0
  81. package/dist/cli/ui.d.ts +7 -0
  82. package/dist/cli/ui.js +72 -0
  83. package/dist/cli/ui.js.map +1 -0
  84. package/dist/cli.d.ts +2 -0
  85. package/dist/cli.js +268 -0
  86. package/dist/cli.js.map +1 -0
  87. package/dist/config.d.ts +4 -0
  88. package/dist/config.js +38 -0
  89. package/dist/config.js.map +1 -0
  90. package/dist/discovery.d.ts +5 -0
  91. package/dist/discovery.js +21 -0
  92. package/dist/discovery.js.map +1 -0
  93. package/dist/discovery.test.d.ts +1 -0
  94. package/dist/discovery.test.js +46 -0
  95. package/dist/discovery.test.js.map +1 -0
  96. package/dist/http.d.ts +32 -0
  97. package/dist/http.js +261 -0
  98. package/dist/http.js.map +1 -0
  99. package/dist/http.test.d.ts +1 -0
  100. package/dist/http.test.js +135 -0
  101. package/dist/http.test.js.map +1 -0
  102. package/dist/schema/ark.js.map +1 -0
  103. package/dist/schema/index.js.map +1 -0
  104. package/dist/schema/routes.js.map +1 -0
  105. package/{packages/schema/dist → dist/schema}/schemas.d.ts +0 -39
  106. package/{packages/schema/dist → dist/schema}/schemas.js +0 -22
  107. package/dist/schema/schemas.js.map +1 -0
  108. package/dist/schema/textFiles.js.map +1 -0
  109. package/dist/schema/textFiles.test.d.ts +1 -0
  110. package/dist/schema/textFiles.test.js +20 -0
  111. package/dist/schema/textFiles.test.js.map +1 -0
  112. package/dist/skills.d.ts +43 -0
  113. package/dist/skills.js +163 -0
  114. package/dist/skills.js.map +1 -0
  115. package/dist/skills.test.d.ts +1 -0
  116. package/dist/skills.test.js +144 -0
  117. package/dist/skills.test.js.map +1 -0
  118. package/dist/types.d.ts +7 -0
  119. package/dist/types.js +2 -0
  120. package/dist/types.js.map +1 -0
  121. package/package.json +27 -70
  122. package/.env.local.example +0 -19
  123. package/.github/workflows/ci.yml +0 -40
  124. package/.oxlintrc.json +0 -3
  125. package/AGENTS.md +0 -45
  126. package/CHANGELOG.md +0 -138
  127. package/DEPRECATIONS.md +0 -7
  128. package/biome.json +0 -41
  129. package/convex/_generated/api.d.ts +0 -153
  130. package/convex/_generated/api.js +0 -23
  131. package/convex/_generated/dataModel.d.ts +0 -60
  132. package/convex/_generated/server.d.ts +0 -143
  133. package/convex/_generated/server.js +0 -93
  134. package/convex/auth.config.ts +0 -8
  135. package/convex/auth.ts +0 -19
  136. package/convex/comments.ts +0 -88
  137. package/convex/crons.ts +0 -34
  138. package/convex/devSeed.ts +0 -459
  139. package/convex/devSeedExtra.ts +0 -541
  140. package/convex/downloads.ts +0 -78
  141. package/convex/githubBackups.ts +0 -170
  142. package/convex/githubBackupsNode.ts +0 -183
  143. package/convex/githubImport.ts +0 -317
  144. package/convex/githubSoulBackups.ts +0 -170
  145. package/convex/githubSoulBackupsNode.ts +0 -186
  146. package/convex/http.ts +0 -194
  147. package/convex/httpApi.handlers.test.ts +0 -488
  148. package/convex/httpApi.test.ts +0 -70
  149. package/convex/httpApi.ts +0 -305
  150. package/convex/httpApiV1.handlers.test.ts +0 -584
  151. package/convex/httpApiV1.ts +0 -1172
  152. package/convex/leaderboards.ts +0 -39
  153. package/convex/lib/access.ts +0 -36
  154. package/convex/lib/apiTokenAuth.ts +0 -36
  155. package/convex/lib/badges.ts +0 -50
  156. package/convex/lib/changelog.test.ts +0 -34
  157. package/convex/lib/changelog.ts +0 -278
  158. package/convex/lib/embeddings.ts +0 -38
  159. package/convex/lib/githubBackup.ts +0 -443
  160. package/convex/lib/githubImport.test.ts +0 -247
  161. package/convex/lib/githubImport.ts +0 -425
  162. package/convex/lib/githubSoulBackup.ts +0 -443
  163. package/convex/lib/leaderboards.ts +0 -103
  164. package/convex/lib/moderation.ts +0 -42
  165. package/convex/lib/public.ts +0 -89
  166. package/convex/lib/searchText.test.ts +0 -46
  167. package/convex/lib/searchText.ts +0 -27
  168. package/convex/lib/skillBackfill.test.ts +0 -34
  169. package/convex/lib/skillBackfill.ts +0 -67
  170. package/convex/lib/skillPublish.test.ts +0 -28
  171. package/convex/lib/skillPublish.ts +0 -284
  172. package/convex/lib/skillStats.ts +0 -80
  173. package/convex/lib/skills.test.ts +0 -197
  174. package/convex/lib/skills.ts +0 -273
  175. package/convex/lib/soulChangelog.ts +0 -273
  176. package/convex/lib/soulPublish.ts +0 -236
  177. package/convex/lib/tokens.test.ts +0 -33
  178. package/convex/lib/tokens.ts +0 -51
  179. package/convex/lib/webhooks.test.ts +0 -91
  180. package/convex/lib/webhooks.ts +0 -112
  181. package/convex/maintenance.test.ts +0 -270
  182. package/convex/maintenance.ts +0 -840
  183. package/convex/rateLimits.ts +0 -50
  184. package/convex/schema.ts +0 -472
  185. package/convex/search.test.ts +0 -12
  186. package/convex/search.ts +0 -254
  187. package/convex/seed.test.ts +0 -37
  188. package/convex/seed.ts +0 -254
  189. package/convex/seedSouls.ts +0 -111
  190. package/convex/skillStatEvents.ts +0 -568
  191. package/convex/skills.ts +0 -1606
  192. package/convex/soulComments.ts +0 -88
  193. package/convex/soulDownloads.ts +0 -14
  194. package/convex/soulStars.ts +0 -71
  195. package/convex/souls.ts +0 -570
  196. package/convex/stars.ts +0 -108
  197. package/convex/statsMaintenance.ts +0 -205
  198. package/convex/telemetry.ts +0 -434
  199. package/convex/tokens.ts +0 -88
  200. package/convex/tsconfig.json +0 -7
  201. package/convex/uploads.ts +0 -20
  202. package/convex/users.ts +0 -122
  203. package/convex/webhooks.ts +0 -50
  204. package/convex.json +0 -3
  205. package/docs/README.md +0 -32
  206. package/docs/api.md +0 -51
  207. package/docs/architecture.md +0 -61
  208. package/docs/auth.md +0 -54
  209. package/docs/cli.md +0 -117
  210. package/docs/deploy.md +0 -78
  211. package/docs/diffing.md +0 -84
  212. package/docs/github-import.md +0 -171
  213. package/docs/http-api.md +0 -187
  214. package/docs/manual-testing.md +0 -64
  215. package/docs/mintlify.md +0 -43
  216. package/docs/quickstart.md +0 -120
  217. package/docs/skill-format.md +0 -58
  218. package/docs/soul-format.md +0 -37
  219. package/docs/spec.md +0 -177
  220. package/docs/telemetry.md +0 -91
  221. package/docs/troubleshooting.md +0 -49
  222. package/docs/webhook.md +0 -51
  223. package/e2e/menu-smoke.pw.test.ts +0 -49
  224. package/e2e/pilothub.e2e.test.ts +0 -494
  225. package/e2e/search-exact.pw.test.ts +0 -97
  226. package/packages/pilothub/LICENSE +0 -22
  227. package/packages/pilothub/README.md +0 -57
  228. package/packages/pilothub/package.json +0 -41
  229. package/packages/pilothub/src/browserAuth.test.ts +0 -96
  230. package/packages/pilothub/src/browserAuth.ts +0 -174
  231. package/packages/pilothub/src/cli/buildInfo.ts +0 -94
  232. package/packages/pilothub/src/cli/commands/auth.ts +0 -97
  233. package/packages/pilothub/src/cli/commands/delete.test.ts +0 -73
  234. package/packages/pilothub/src/cli/commands/delete.ts +0 -83
  235. package/packages/pilothub/src/cli/commands/publish.test.ts +0 -122
  236. package/packages/pilothub/src/cli/commands/publish.ts +0 -108
  237. package/packages/pilothub/src/cli/commands/skills.test.ts +0 -191
  238. package/packages/pilothub/src/cli/commands/skills.ts +0 -380
  239. package/packages/pilothub/src/cli/commands/star.ts +0 -46
  240. package/packages/pilothub/src/cli/commands/sync.test.ts +0 -310
  241. package/packages/pilothub/src/cli/commands/sync.ts +0 -200
  242. package/packages/pilothub/src/cli/commands/syncHelpers.test.ts +0 -26
  243. package/packages/pilothub/src/cli/commands/syncHelpers.ts +0 -427
  244. package/packages/pilothub/src/cli/commands/syncTypes.ts +0 -27
  245. package/packages/pilothub/src/cli/commands/unstar.ts +0 -48
  246. package/packages/pilothub/src/cli/helpStyle.ts +0 -45
  247. package/packages/pilothub/src/cli/pilotbotConfig.test.ts +0 -159
  248. package/packages/pilothub/src/cli/pilotbotConfig.ts +0 -147
  249. package/packages/pilothub/src/cli/registry.test.ts +0 -63
  250. package/packages/pilothub/src/cli/registry.ts +0 -43
  251. package/packages/pilothub/src/cli/scanSkills.test.ts +0 -64
  252. package/packages/pilothub/src/cli/scanSkills.ts +0 -84
  253. package/packages/pilothub/src/cli/slug.ts +0 -16
  254. package/packages/pilothub/src/cli/types.ts +0 -12
  255. package/packages/pilothub/src/cli/ui.ts +0 -75
  256. package/packages/pilothub/src/cli.ts +0 -311
  257. package/packages/pilothub/src/config.ts +0 -36
  258. package/packages/pilothub/src/discovery.test.ts +0 -75
  259. package/packages/pilothub/src/discovery.ts +0 -19
  260. package/packages/pilothub/src/http.test.ts +0 -156
  261. package/packages/pilothub/src/http.ts +0 -301
  262. package/packages/pilothub/src/schema/ark.ts +0 -29
  263. package/packages/pilothub/src/schema/index.ts +0 -5
  264. package/packages/pilothub/src/schema/routes.ts +0 -22
  265. package/packages/pilothub/src/schema/schemas.ts +0 -260
  266. package/packages/pilothub/src/schema/textFiles.test.ts +0 -23
  267. package/packages/pilothub/src/schema/textFiles.ts +0 -66
  268. package/packages/pilothub/src/skills.test.ts +0 -191
  269. package/packages/pilothub/src/skills.ts +0 -172
  270. package/packages/pilothub/src/types.ts +0 -10
  271. package/packages/pilothub/tsconfig.json +0 -14
  272. package/packages/schema/README.md +0 -3
  273. package/packages/schema/dist/ark.js.map +0 -1
  274. package/packages/schema/dist/index.js.map +0 -1
  275. package/packages/schema/dist/routes.js.map +0 -1
  276. package/packages/schema/dist/schemas.js.map +0 -1
  277. package/packages/schema/dist/textFiles.js.map +0 -1
  278. package/packages/schema/package.json +0 -26
  279. package/packages/schema/src/ark.ts +0 -29
  280. package/packages/schema/src/index.ts +0 -5
  281. package/packages/schema/src/routes.ts +0 -22
  282. package/packages/schema/src/schemas.test.ts +0 -123
  283. package/packages/schema/src/schemas.ts +0 -287
  284. package/packages/schema/src/textFiles.test.ts +0 -23
  285. package/packages/schema/src/textFiles.ts +0 -66
  286. package/packages/schema/tsconfig.json +0 -15
  287. package/pilothub +0 -46
  288. package/playwright.config.ts +0 -33
  289. package/public/.well-known/pilothub.json +0 -6
  290. package/public/api/v1/openapi.json +0 -379
  291. package/public/favicon.ico +0 -0
  292. package/public/logo192.png +0 -0
  293. package/public/logo512.png +0 -0
  294. package/public/manifest.json +0 -25
  295. package/public/og.png +0 -0
  296. package/public/og.svg +0 -98
  297. package/public/pilot-logo.png +0 -0
  298. package/public/pilot-mark.png +0 -0
  299. package/public/robots.txt +0 -3
  300. package/public/tanstack-circle-logo.png +0 -0
  301. package/public/tanstack-word-logo-white.svg +0 -1
  302. package/scripts/check-peer-deps.ts +0 -56
  303. package/scripts/docs-list.ts +0 -148
  304. package/scripts/run-playwright-local.sh +0 -14
  305. package/server/og/fetchSkillOgMeta.ts +0 -27
  306. package/server/og/fetchSoulOgMeta.ts +0 -27
  307. package/server/og/ogAssets.ts +0 -80
  308. package/server/og/skillOgSvg.test.ts +0 -59
  309. package/server/og/skillOgSvg.ts +0 -258
  310. package/server/og/soulOgSvg.ts +0 -209
  311. package/server/routes/og/skill.png.ts +0 -103
  312. package/server/routes/og/soul.png.ts +0 -111
  313. package/src/__tests__/skill-detail-page.test.tsx +0 -86
  314. package/src/__tests__/skills-index.test.tsx +0 -145
  315. package/src/__tests__/upload.route.test.tsx +0 -228
  316. package/src/components/AppProviders.tsx +0 -19
  317. package/src/components/ClientOnly.tsx +0 -18
  318. package/src/components/Footer.tsx +0 -29
  319. package/src/components/Header.tsx +0 -295
  320. package/src/components/InstallSwitcher.tsx +0 -53
  321. package/src/components/SkillCard.tsx +0 -36
  322. package/src/components/SkillDetailPage.tsx +0 -817
  323. package/src/components/SkillDiffCard.tsx +0 -485
  324. package/src/components/SoulCard.tsx +0 -19
  325. package/src/components/SoulDetailPage.tsx +0 -263
  326. package/src/components/UserBootstrap.tsx +0 -18
  327. package/src/components/ui/dropdown-menu.tsx +0 -67
  328. package/src/components/ui/toggle-group.tsx +0 -35
  329. package/src/convex/client.ts +0 -3
  330. package/src/lib/badges.ts +0 -29
  331. package/src/lib/diffing.test.ts +0 -163
  332. package/src/lib/diffing.ts +0 -106
  333. package/src/lib/gravatar.test.ts +0 -9
  334. package/src/lib/gravatar.ts +0 -158
  335. package/src/lib/og.test.ts +0 -142
  336. package/src/lib/og.ts +0 -156
  337. package/src/lib/publicUser.ts +0 -39
  338. package/src/lib/roles.ts +0 -19
  339. package/src/lib/site.test.ts +0 -130
  340. package/src/lib/site.ts +0 -84
  341. package/src/lib/theme-transition.test.ts +0 -134
  342. package/src/lib/theme-transition.ts +0 -134
  343. package/src/lib/theme.test.tsx +0 -88
  344. package/src/lib/theme.ts +0 -43
  345. package/src/lib/uploadFiles.jsdom.test.ts +0 -33
  346. package/src/lib/uploadFiles.test.ts +0 -123
  347. package/src/lib/uploadFiles.ts +0 -245
  348. package/src/lib/uploadUtils.test.ts +0 -78
  349. package/src/lib/uploadUtils.ts +0 -93
  350. package/src/lib/useAuthStatus.ts +0 -12
  351. package/src/lib/utils.test.ts +0 -9
  352. package/src/lib/utils.ts +0 -6
  353. package/src/logo.svg +0 -12
  354. package/src/routeTree.gen.ts +0 -345
  355. package/src/router.tsx +0 -17
  356. package/src/routes/$owner/$slug.tsx +0 -55
  357. package/src/routes/__root.tsx +0 -136
  358. package/src/routes/admin.tsx +0 -11
  359. package/src/routes/cli/auth.tsx +0 -168
  360. package/src/routes/dashboard.tsx +0 -97
  361. package/src/routes/import.tsx +0 -415
  362. package/src/routes/index.tsx +0 -252
  363. package/src/routes/management.tsx +0 -529
  364. package/src/routes/settings.tsx +0 -203
  365. package/src/routes/skills/index.tsx +0 -422
  366. package/src/routes/souls/$slug.tsx +0 -55
  367. package/src/routes/souls/index.tsx +0 -243
  368. package/src/routes/stars.tsx +0 -68
  369. package/src/routes/u/$handle.tsx +0 -307
  370. package/src/routes/upload/utils.ts +0 -81
  371. package/src/routes/upload.tsx +0 -499
  372. package/src/styles.css +0 -2718
  373. package/tsconfig.json +0 -24
  374. package/tsconfig.oxlint.json +0 -16
  375. package/vercel.json +0 -8
  376. package/vite.config.ts +0 -48
  377. package/vitest.config.ts +0 -47
  378. package/vitest.e2e.config.ts +0 -11
  379. package/vitest.setup.ts +0 -1
  380. /package/{packages/pilothub/bin → bin}/pilothub.js +0 -0
  381. /package/{packages/schema/dist → dist/schema}/ark.d.ts +0 -0
  382. /package/{packages/schema/dist → dist/schema}/ark.js +0 -0
  383. /package/{packages/schema/dist → dist/schema}/index.d.ts +0 -0
  384. /package/{packages/schema/dist → dist/schema}/index.js +0 -0
  385. /package/{packages/schema/dist → dist/schema}/routes.d.ts +0 -0
  386. /package/{packages/schema/dist → dist/schema}/routes.js +0 -0
  387. /package/{packages/schema/dist → dist/schema}/textFiles.d.ts +0 -0
  388. /package/{packages/schema/dist → dist/schema}/textFiles.js +0 -0
package/src/lib/site.ts DELETED
@@ -1,84 +0,0 @@
1
- export type SiteMode = 'skills' | 'souls'
2
-
3
- const DEFAULT_PILOTHUB_SITE_URL = 'https://pilothub.com'
4
- const DEFAULT_ONLYCRABS_SITE_URL = 'https://onlycrabs.ai'
5
- const DEFAULT_ONLYCRABS_HOST = 'onlycrabs.ai'
6
-
7
- export function getPilotHubSiteUrl() {
8
- return import.meta.env.VITE_SITE_URL ?? DEFAULT_PILOTHUB_SITE_URL
9
- }
10
-
11
- export function getOnlyCrabsSiteUrl() {
12
- const explicit = import.meta.env.VITE_SOULHUB_SITE_URL
13
- if (explicit) return explicit
14
-
15
- const siteUrl = import.meta.env.VITE_SITE_URL
16
- if (siteUrl) {
17
- try {
18
- const url = new URL(siteUrl)
19
- if (
20
- url.hostname === 'localhost' ||
21
- url.hostname === '127.0.0.1' ||
22
- url.hostname === '0.0.0.0'
23
- ) {
24
- return url.origin
25
- }
26
- } catch {
27
- // ignore invalid URLs, fall through to default
28
- }
29
- }
30
-
31
- return DEFAULT_ONLYCRABS_SITE_URL
32
- }
33
-
34
- export function getOnlyCrabsHost() {
35
- return import.meta.env.VITE_SOULHUB_HOST ?? DEFAULT_ONLYCRABS_HOST
36
- }
37
-
38
- export function detectSiteMode(host?: string | null): SiteMode {
39
- if (!host) return 'skills'
40
- const onlyCrabsHost = getOnlyCrabsHost().toLowerCase()
41
- const lower = host.toLowerCase()
42
- if (lower === onlyCrabsHost || lower.endsWith(`.${onlyCrabsHost}`)) return 'souls'
43
- return 'skills'
44
- }
45
-
46
- export function detectSiteModeFromUrl(value?: string | null): SiteMode {
47
- if (!value) return 'skills'
48
- try {
49
- const host = new URL(value).hostname
50
- return detectSiteMode(host)
51
- } catch {
52
- return detectSiteMode(value)
53
- }
54
- }
55
-
56
- export function getSiteMode(): SiteMode {
57
- if (typeof window !== 'undefined') {
58
- return detectSiteMode(window.location.hostname)
59
- }
60
- const forced = import.meta.env.VITE_SITE_MODE
61
- if (forced === 'souls' || forced === 'skills') return forced
62
-
63
- const onlyCrabsSite = import.meta.env.VITE_SOULHUB_SITE_URL
64
- if (onlyCrabsSite) return detectSiteModeFromUrl(onlyCrabsSite)
65
-
66
- const siteUrl = import.meta.env.VITE_SITE_URL ?? process.env.SITE_URL
67
- if (siteUrl) return detectSiteModeFromUrl(siteUrl)
68
-
69
- return 'skills'
70
- }
71
-
72
- export function getSiteName(mode: SiteMode = getSiteMode()) {
73
- return mode === 'souls' ? 'SoulHub' : 'PilotHub'
74
- }
75
-
76
- export function getSiteDescription(mode: SiteMode = getSiteMode()) {
77
- return mode === 'souls'
78
- ? 'SoulHub — the home for SOUL.md bundles and personal system lore.'
79
- : 'PilotHub — a fast skill registry for agents, with vector search.'
80
- }
81
-
82
- export function getSiteUrlForMode(mode: SiteMode = getSiteMode()) {
83
- return mode === 'souls' ? getOnlyCrabsSiteUrl() : getPilotHubSiteUrl()
84
- }
@@ -1,134 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest'
2
- import { startThemeTransition } from './theme-transition'
3
-
4
- describe('startThemeTransition', () => {
5
- it('no-ops when theme does not change', () => {
6
- const setTheme = vi.fn()
7
- startThemeTransition({
8
- currentTheme: 'dark',
9
- nextTheme: 'dark',
10
- setTheme,
11
- })
12
- expect(setTheme).not.toHaveBeenCalled()
13
- })
14
-
15
- it('applies theme without document (SSR)', () => {
16
- const original = Object.getOwnPropertyDescriptor(globalThis, 'document')
17
- Object.defineProperty(globalThis, 'document', { value: undefined, configurable: true })
18
-
19
- try {
20
- const calls: string[] = []
21
- const setTheme = vi.fn()
22
- startThemeTransition({
23
- currentTheme: 'light',
24
- nextTheme: 'dark',
25
- setTheme,
26
- onBeforeThemeChange: () => calls.push('before'),
27
- onAfterThemeChange: () => calls.push('after'),
28
- })
29
- expect(calls).toEqual(['before', 'after'])
30
- expect(setTheme).toHaveBeenCalledWith('dark')
31
- } finally {
32
- if (original) Object.defineProperty(globalThis, 'document', original)
33
- else delete (globalThis as unknown as { document?: unknown }).document
34
- }
35
- })
36
-
37
- it('skips view-transition when prefers reduced motion', () => {
38
- const setTheme = vi.fn()
39
- const root = document.documentElement
40
-
41
- window.matchMedia = vi.fn(() => ({ matches: true }) as unknown as MediaQueryList)
42
- ;(document as unknown as { startViewTransition?: unknown }).startViewTransition = vi.fn()
43
-
44
- startThemeTransition({
45
- currentTheme: 'light',
46
- nextTheme: 'dark',
47
- setTheme,
48
- context: { pointerClientX: 10, pointerClientY: 10 },
49
- })
50
-
51
- expect(setTheme).toHaveBeenCalledWith('dark')
52
- expect(root.classList.contains('theme-transition')).toBe(false)
53
- expect(
54
- (document as unknown as { startViewTransition?: unknown }).startViewTransition,
55
- ).not.toHaveBeenCalled()
56
- })
57
-
58
- it('uses view-transition when available', async () => {
59
- const setTheme = vi.fn()
60
- const root = document.documentElement
61
-
62
- window.matchMedia = vi.fn(() => ({ matches: false }) as unknown as MediaQueryList)
63
-
64
- ;(
65
- document as unknown as {
66
- startViewTransition?: (callback: () => void) => { finished: Promise<void> }
67
- }
68
- ).startViewTransition = (callback) => {
69
- callback()
70
- return { finished: Promise.resolve() }
71
- }
72
-
73
- startThemeTransition({
74
- currentTheme: 'light',
75
- nextTheme: 'dark',
76
- setTheme,
77
- context: { pointerClientX: 10, pointerClientY: 20 },
78
- })
79
-
80
- expect(setTheme).toHaveBeenCalledWith('dark')
81
- expect(root.classList.contains('theme-transition')).toBe(true)
82
-
83
- await new Promise((r) => setTimeout(r, 0))
84
- expect(root.classList.contains('theme-transition')).toBe(false)
85
- expect(root.style.getPropertyValue('--theme-switch-x')).toBe('')
86
- expect(root.style.getPropertyValue('--theme-switch-y')).toBe('')
87
- })
88
-
89
- it('cleans up when view-transition does not provide finished', () => {
90
- const setTheme = vi.fn()
91
- const root = document.documentElement
92
-
93
- window.matchMedia = vi.fn(() => ({ matches: false }) as unknown as MediaQueryList)
94
- ;(
95
- document as unknown as { startViewTransition?: (callback: () => void) => unknown }
96
- ).startViewTransition = (callback) => {
97
- callback()
98
- return {}
99
- }
100
-
101
- startThemeTransition({
102
- currentTheme: 'light',
103
- nextTheme: 'dark',
104
- setTheme,
105
- context: { element: document.body },
106
- })
107
-
108
- expect(setTheme).toHaveBeenCalledWith('dark')
109
- expect(root.classList.contains('theme-transition')).toBe(false)
110
- })
111
-
112
- it('falls back when view-transition throws', () => {
113
- const setTheme = vi.fn()
114
- const root = document.documentElement
115
-
116
- window.matchMedia = vi.fn(() => ({ matches: false }) as unknown as MediaQueryList)
117
- ;(document as unknown as { startViewTransition?: () => never }).startViewTransition = () => {
118
- throw new Error('nope')
119
- }
120
-
121
- const element = document.createElement('button')
122
- element.getBoundingClientRect = () => ({ left: 10, top: 10, width: 10, height: 10 }) as DOMRect
123
-
124
- startThemeTransition({
125
- currentTheme: 'light',
126
- nextTheme: 'dark',
127
- setTheme,
128
- context: { element },
129
- })
130
-
131
- expect(setTheme).toHaveBeenCalledWith('dark')
132
- expect(root.classList.contains('theme-transition')).toBe(false)
133
- })
134
- })
@@ -1,134 +0,0 @@
1
- import { flushSync } from 'react-dom'
2
-
3
- export type ThemeValue = 'light' | 'dark' | 'system' | (string & {})
4
-
5
- export type ThemeTransitionContext = {
6
- element?: HTMLElement | null
7
- pointerClientX?: number
8
- pointerClientY?: number
9
- }
10
-
11
- export type ThemeTransitionOptions = {
12
- nextTheme: ThemeValue
13
- setTheme: (theme: ThemeValue) => void
14
- context?: ThemeTransitionContext | undefined
15
- onBeforeThemeChange?: () => void
16
- onAfterThemeChange?: () => void
17
- currentTheme?: ThemeValue | null
18
- }
19
-
20
- type DocumentWithViewTransition = Document & {
21
- startViewTransition?: (callback: () => void) => {
22
- finished: Promise<void>
23
- }
24
- }
25
-
26
- type WindowWithMatchMedia = Window & {
27
- matchMedia: (query: string) => MediaQueryList
28
- }
29
-
30
- const clamp01 = (value: number) => {
31
- if (Number.isNaN(value)) return 0.5
32
- if (value <= 0) return 0
33
- if (value >= 1) return 1
34
- return value
35
- }
36
-
37
- const resolveWindow = (): WindowWithMatchMedia | undefined =>
38
- globalThis.window as WindowWithMatchMedia | undefined
39
-
40
- const hasReducedMotionPreference = (): boolean => {
41
- const currentWindow = resolveWindow()
42
- if (!currentWindow || typeof currentWindow.matchMedia !== 'function') return false
43
- return currentWindow.matchMedia('(prefers-reduced-motion: reduce)').matches ?? false
44
- }
45
-
46
- const cleanupThemeTransition = (root: HTMLElement) => {
47
- root.classList.remove('theme-transition')
48
- root.style.removeProperty('--theme-switch-x')
49
- root.style.removeProperty('--theme-switch-y')
50
- }
51
-
52
- export const startThemeTransition = ({
53
- nextTheme,
54
- setTheme,
55
- context,
56
- onBeforeThemeChange,
57
- onAfterThemeChange,
58
- currentTheme,
59
- }: ThemeTransitionOptions) => {
60
- if (currentTheme === nextTheme) return
61
-
62
- const documentReference = globalThis.document ?? null
63
- if (!documentReference) {
64
- onBeforeThemeChange?.()
65
- setTheme(nextTheme)
66
- onAfterThemeChange?.()
67
- return
68
- }
69
-
70
- const root = documentReference.documentElement
71
- const document_ = documentReference as DocumentWithViewTransition
72
- const prefersReducedMotion = hasReducedMotionPreference()
73
-
74
- const applyTheme = () => {
75
- onBeforeThemeChange?.()
76
- flushSync(() => {
77
- setTheme(nextTheme)
78
- })
79
- onAfterThemeChange?.()
80
- }
81
-
82
- const canUseViewTransition = Boolean(document_.startViewTransition) && !prefersReducedMotion
83
- if (canUseViewTransition) {
84
- let xPercent = 0.5
85
- let yPercent = 0.5
86
-
87
- const currentWindow = resolveWindow()
88
- if (
89
- context?.pointerClientX !== undefined &&
90
- context?.pointerClientY !== undefined &&
91
- currentWindow
92
- ) {
93
- xPercent = clamp01(context.pointerClientX / currentWindow.innerWidth)
94
- yPercent = clamp01(context.pointerClientY / currentWindow.innerHeight)
95
- } else if (context?.element) {
96
- const rect = context.element.getBoundingClientRect()
97
- if (rect.width > 0 && rect.height > 0 && currentWindow) {
98
- xPercent = clamp01((rect.left + rect.width / 2) / currentWindow.innerWidth)
99
- yPercent = clamp01((rect.top + rect.height / 2) / currentWindow.innerHeight)
100
- }
101
- }
102
-
103
- root.style.setProperty('--theme-switch-x', `${xPercent * 100}%`)
104
- root.style.setProperty('--theme-switch-y', `${yPercent * 100}%`)
105
- root.classList.add('theme-transition')
106
-
107
- try {
108
- const transition = document_.startViewTransition?.(() => {
109
- applyTheme()
110
- })
111
- if (transition?.finished === undefined) {
112
- cleanupThemeTransition(root)
113
- } else {
114
- const handleTransitionFinish = async () => {
115
- try {
116
- await transition.finished
117
- } catch {
118
- // swallow transition cancellation errors
119
- } finally {
120
- cleanupThemeTransition(root)
121
- }
122
- }
123
- void handleTransitionFinish()
124
- }
125
- } catch {
126
- cleanupThemeTransition(root)
127
- applyTheme()
128
- }
129
- return
130
- }
131
-
132
- applyTheme()
133
- cleanupThemeTransition(root)
134
- }
@@ -1,88 +0,0 @@
1
- import { fireEvent, render, screen, waitFor } from '@testing-library/react'
2
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3
- import { applyTheme, getStoredTheme, useThemeMode } from './theme'
4
-
5
- describe('theme', () => {
6
- let store: Record<string, string>
7
-
8
- function Harness() {
9
- const { mode, setMode } = useThemeMode()
10
- return (
11
- <div>
12
- <div data-testid="mode">{mode}</div>
13
- <button type="button" onClick={() => setMode('dark')}>
14
- dark
15
- </button>
16
- </div>
17
- )
18
- }
19
-
20
- beforeEach(() => {
21
- store = {}
22
- Object.defineProperty(window, 'localStorage', {
23
- value: {
24
- getItem: (key: string) => (key in store ? store[key] : null),
25
- setItem: (key: string, value: string) => {
26
- store[key] = String(value)
27
- },
28
- removeItem: (key: string) => {
29
- delete store[key]
30
- },
31
- clear: () => {
32
- store = {}
33
- },
34
- },
35
- configurable: true,
36
- })
37
- })
38
-
39
- afterEach(() => {
40
- document.documentElement.classList.remove('dark')
41
- delete document.documentElement.dataset.theme
42
- window.localStorage.clear()
43
- vi.unstubAllGlobals()
44
- })
45
-
46
- it('reads stored theme with fallback', () => {
47
- expect(getStoredTheme()).toBe('system')
48
- window.localStorage.setItem('pilothub-theme', 'dark')
49
- expect(getStoredTheme()).toBe('dark')
50
- window.localStorage.setItem('pilothub-theme', 'nope')
51
- expect(getStoredTheme()).toBe('system')
52
- })
53
-
54
- it('applies theme and toggles dark class', () => {
55
- applyTheme('dark')
56
- expect(document.documentElement.dataset.theme).toBe('dark')
57
- expect(document.documentElement.classList.contains('dark')).toBe(true)
58
-
59
- applyTheme('light')
60
- expect(document.documentElement.dataset.theme).toBe('light')
61
- expect(document.documentElement.classList.contains('dark')).toBe(false)
62
- })
63
-
64
- it('resolves system theme via matchMedia', () => {
65
- vi.stubGlobal('matchMedia', () => ({
66
- matches: true,
67
- addEventListener: vi.fn(),
68
- removeEventListener: vi.fn(),
69
- }))
70
- applyTheme('system')
71
- expect(document.documentElement.dataset.theme).toBe('dark')
72
- })
73
-
74
- it('useThemeMode persists and applies mode', async () => {
75
- vi.stubGlobal('matchMedia', () => ({
76
- matches: false,
77
- addEventListener: vi.fn(),
78
- removeEventListener: vi.fn(),
79
- }))
80
- render(<Harness />)
81
- expect(screen.getByTestId('mode').textContent).toBe('system')
82
- fireEvent.click(screen.getByRole('button', { name: 'dark' }))
83
- await waitFor(() => {
84
- expect(document.documentElement.dataset.theme).toBe('dark')
85
- })
86
- expect(window.localStorage.getItem('pilothub-theme')).toBe('dark')
87
- })
88
- })
package/src/lib/theme.ts DELETED
@@ -1,43 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
-
3
- export type ThemeMode = 'system' | 'light' | 'dark'
4
-
5
- const THEME_KEY = 'pilothub-theme'
6
-
7
- export function getStoredTheme(): ThemeMode {
8
- if (typeof window === 'undefined') return 'system'
9
- const stored = window.localStorage.getItem(THEME_KEY)
10
- if (stored === 'light' || stored === 'dark' || stored === 'system') return stored
11
- return 'system'
12
- }
13
-
14
- function resolveTheme(mode: ThemeMode) {
15
- if (mode !== 'system') return mode
16
- if (typeof window === 'undefined') return 'light'
17
- return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
18
- }
19
-
20
- export function applyTheme(mode: ThemeMode) {
21
- if (typeof document === 'undefined') return
22
- const resolved = resolveTheme(mode)
23
- document.documentElement.dataset.theme = resolved
24
- document.documentElement.classList.toggle('dark', resolved === 'dark')
25
- }
26
-
27
- export function useThemeMode() {
28
- const [mode, setMode] = useState<ThemeMode>(() => getStoredTheme())
29
-
30
- useEffect(() => {
31
- applyTheme(mode)
32
- if (typeof window !== 'undefined') {
33
- window.localStorage.setItem(THEME_KEY, mode)
34
- }
35
- if (mode !== 'system' || typeof window === 'undefined') return
36
- const media = window.matchMedia('(prefers-color-scheme: dark)')
37
- const handler = () => applyTheme(mode)
38
- media.addEventListener('change', handler)
39
- return () => media.removeEventListener('change', handler)
40
- }, [mode])
41
-
42
- return { mode, setMode }
43
- }
@@ -1,33 +0,0 @@
1
- import { strToU8, unzipSync, zipSync } from 'fflate'
2
- import { describe, expect, it } from 'vitest'
3
-
4
- import { expandFiles } from './uploadFiles'
5
-
6
- function readWithFileReader(blob: Blob) {
7
- return new Promise<ArrayBuffer>((resolve, reject) => {
8
- const reader = new FileReader()
9
- reader.onerror = () => reject(reader.error ?? new Error('Could not read blob.'))
10
- reader.onload = () => resolve(reader.result as ArrayBuffer)
11
- reader.readAsArrayBuffer(blob)
12
- })
13
- }
14
-
15
- describe('expandFiles (jsdom)', () => {
16
- it('expands zip archives using FileReader fallback', async () => {
17
- const zip = zipSync({
18
- 'hetzner-cloud-skill/SKILL.md': new Uint8Array(strToU8('hello')),
19
- 'hetzner-cloud-skill/notes.txt': new Uint8Array(strToU8('notes')),
20
- })
21
- const zipBytes = Uint8Array.from(zip).buffer
22
- const zipFile = new File([zipBytes], 'bundle.zip', { type: 'application/zip' })
23
-
24
- const readerBuffer = await readWithFileReader(zipFile)
25
- const entries = unzipSync(new Uint8Array(readerBuffer))
26
- expect(Object.keys(entries)).toEqual(
27
- expect.arrayContaining(['hetzner-cloud-skill/SKILL.md', 'hetzner-cloud-skill/notes.txt']),
28
- )
29
-
30
- const expanded = await expandFiles([zipFile])
31
- expect(expanded.map((file) => file.name)).toEqual(['SKILL.md', 'notes.txt'])
32
- })
33
- })
@@ -1,123 +0,0 @@
1
- /* @vitest-environment node */
2
- import { gzipSync, strToU8, zipSync } from 'fflate'
3
- import { describe, expect, it } from 'vitest'
4
- import { expandFiles } from './uploadFiles'
5
-
6
- if (typeof File === 'undefined') {
7
- class NodeFile extends Blob {
8
- name: string
9
- lastModified: number
10
-
11
- constructor(parts: BlobPart[], name: string, options?: FilePropertyBag) {
12
- super(parts, options)
13
- this.name = name
14
- this.lastModified = options?.lastModified ?? Date.now()
15
- }
16
- }
17
- // @ts-expect-error Node test environment polyfill
18
- globalThis.File = NodeFile
19
- }
20
-
21
- function buildTar(entries: Array<{ name: string; content: string }>) {
22
- const blocks: Uint8Array[] = []
23
- for (const entry of entries) {
24
- const content = strToU8(entry.content)
25
- const header = new Uint8Array(512)
26
- writeString(header, entry.name, 0, 100)
27
- writeString(header, '0000777', 100, 8)
28
- writeString(header, '0000000', 108, 8)
29
- writeString(header, '0000000', 116, 8)
30
- writeString(header, content.length.toString(8).padStart(11, '0'), 124, 12)
31
- writeString(header, '00000000000', 136, 12)
32
- header[156] = '0'.charCodeAt(0)
33
- writeString(header, 'ustar', 257, 6)
34
- for (let i = 148; i < 156; i += 1) {
35
- header[i] = 32
36
- }
37
- let sum = 0
38
- for (const byte of header) sum += byte
39
- writeString(header, sum.toString(8).padStart(6, '0'), 148, 6)
40
- header[154] = 0
41
- header[155] = 32
42
- blocks.push(header)
43
- blocks.push(content)
44
- const pad = (512 - (content.length % 512)) % 512
45
- if (pad) blocks.push(new Uint8Array(pad))
46
- }
47
- blocks.push(new Uint8Array(1024))
48
- const total = blocks.reduce((sum, block) => sum + block.length, 0)
49
- const buffer = new Uint8Array(total)
50
- let offset = 0
51
- for (const block of blocks) {
52
- buffer.set(block, offset)
53
- offset += block.length
54
- }
55
- return buffer
56
- }
57
-
58
- function writeString(target: Uint8Array, value: string, start: number, length: number) {
59
- const bytes = strToU8(value)
60
- target.set(bytes.subarray(0, length), start)
61
- }
62
-
63
- describe('expandFiles', () => {
64
- it('expands zip archives into files', async () => {
65
- const zip = zipSync({
66
- 'SKILL.md': strToU8('hello'),
67
- 'docs/readme.txt': strToU8('doc'),
68
- })
69
- const zipFile = new File([Uint8Array.from(zip).buffer], 'pack.zip', { type: 'application/zip' })
70
- const result = await expandFiles([zipFile])
71
- expect(result.map((file) => file.name)).toEqual(['SKILL.md', 'docs/readme.txt'])
72
- })
73
-
74
- it('unwraps top-level folders in zip archives', async () => {
75
- const zip = zipSync({
76
- 'hetzner-cloud-skill/SKILL.md': strToU8('hello'),
77
- 'hetzner-cloud-skill/docs/readme.txt': strToU8('doc'),
78
- '__MACOSX/._SKILL.md': strToU8('junk'),
79
- 'hetzner-cloud-skill/.DS_Store': strToU8('junk2'),
80
- 'hetzner-cloud-skill/screenshot.png': strToU8('not-really-a-png'),
81
- })
82
- const zipFile = new File([Uint8Array.from(zip).buffer], 'pack.zip', { type: 'application/zip' })
83
- const result = await expandFiles([zipFile])
84
- expect(result.map((file) => file.name)).toEqual(['SKILL.md', 'docs/readme.txt'])
85
- const png = result.find((file) => file.name.endsWith('.png'))
86
- expect(png).toBeUndefined()
87
- })
88
-
89
- it('expands gzipped tar archives into files', async () => {
90
- const tar = buildTar([
91
- { name: 'SKILL.md', content: 'hi' },
92
- { name: 'notes.txt', content: 'yo' },
93
- ])
94
- const tgz = gzipSync(tar)
95
- const tgzFile = new File([Uint8Array.from(tgz).buffer], 'bundle.tgz', {
96
- type: 'application/gzip',
97
- })
98
- const result = await expandFiles([tgzFile])
99
- expect(result.map((file) => file.name)).toEqual(['SKILL.md', 'notes.txt'])
100
- })
101
-
102
- it('unwraps top-level folders in tar.gz archives', async () => {
103
- const tar = buildTar([
104
- { name: 'skill-folder/SKILL.md', content: 'hi' },
105
- { name: 'skill-folder/notes.txt', content: 'yo' },
106
- ])
107
- const tgz = gzipSync(tar)
108
- const tgzFile = new File([Uint8Array.from(tgz).buffer], 'bundle.tgz', {
109
- type: 'application/gzip',
110
- })
111
- const result = await expandFiles([tgzFile])
112
- expect(result.map((file) => file.name)).toEqual(['SKILL.md', 'notes.txt'])
113
- })
114
-
115
- it('expands .gz single files', async () => {
116
- const gz = gzipSync(strToU8('content'))
117
- const gzFile = new File([Uint8Array.from(gz).buffer], 'skill.md.gz', {
118
- type: 'application/gzip',
119
- })
120
- const result = await expandFiles([gzFile])
121
- expect(result.map((file) => file.name)).toEqual(['skill.md'])
122
- })
123
- })