insforge 1.3.0 → 1.4.8

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 (269) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/auth/package.json +5 -3
  3. package/auth/src/lib/broadcastService.ts +115 -117
  4. package/auth/src/lib/insforge.ts +8 -0
  5. package/auth/src/main.tsx +2 -4
  6. package/auth/src/pages/SignInPage.tsx +60 -60
  7. package/auth/src/pages/SignUpPage.tsx +60 -60
  8. package/auth/src/pages/VerifyEmailPage.tsx +18 -0
  9. package/auth/tsconfig.json +2 -1
  10. package/backend/package.json +10 -6
  11. package/backend/src/api/middlewares/rate-limiters.ts +127 -127
  12. package/backend/src/api/routes/ai/index.routes.ts +475 -468
  13. package/backend/src/api/routes/auth/index.routes.ts +85 -32
  14. package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
  15. package/backend/src/api/routes/database/index.routes.ts +2 -0
  16. package/backend/src/api/routes/database/records.routes.ts +39 -175
  17. package/backend/src/api/routes/database/rpc.routes.ts +69 -0
  18. package/backend/src/api/routes/deployments/index.routes.ts +192 -0
  19. package/backend/src/api/routes/docs/index.routes.ts +3 -2
  20. package/backend/src/api/routes/email/index.routes.ts +35 -35
  21. package/backend/src/api/routes/functions/index.routes.ts +3 -3
  22. package/backend/src/api/routes/metadata/index.routes.ts +26 -0
  23. package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
  24. package/backend/src/infra/database/database.manager.ts +0 -10
  25. package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
  26. package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
  27. package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
  28. package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
  29. package/backend/src/infra/security/token.manager.ts +1 -4
  30. package/backend/src/providers/ai/openrouter.provider.ts +12 -3
  31. package/backend/src/providers/database/base.provider.ts +39 -0
  32. package/backend/src/providers/database/cloud.provider.ts +159 -0
  33. package/backend/src/providers/deployments/vercel.provider.ts +516 -0
  34. package/backend/src/server.ts +19 -7
  35. package/backend/src/services/ai/ai-config.service.ts +6 -6
  36. package/backend/src/services/ai/ai-model.service.ts +60 -60
  37. package/backend/src/services/ai/ai-usage.service.ts +7 -7
  38. package/backend/src/services/ai/chat-completion.service.ts +415 -220
  39. package/backend/src/services/ai/helpers.ts +64 -64
  40. package/backend/src/services/ai/index.ts +13 -13
  41. package/backend/src/services/auth/auth-config.service.ts +4 -4
  42. package/backend/src/services/auth/auth-otp.service.ts +6 -6
  43. package/backend/src/services/auth/auth.service.ts +134 -74
  44. package/backend/src/services/auth/index.ts +4 -4
  45. package/backend/src/services/auth/oauth-config.service.ts +12 -12
  46. package/backend/src/services/database/database-advance.service.ts +19 -55
  47. package/backend/src/services/database/database-table.service.ts +38 -85
  48. package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
  49. package/backend/src/services/deployments/deployment.service.ts +693 -0
  50. package/backend/src/services/functions/function.service.ts +61 -41
  51. package/backend/src/services/logs/audit.service.ts +10 -10
  52. package/backend/src/services/secrets/secret.service.ts +101 -27
  53. package/backend/src/services/storage/storage.service.ts +30 -30
  54. package/backend/src/services/usage/usage.service.ts +6 -6
  55. package/backend/src/types/ai.ts +8 -0
  56. package/backend/src/types/auth.ts +5 -1
  57. package/backend/src/types/database.ts +2 -0
  58. package/backend/src/types/deployments.ts +33 -0
  59. package/backend/src/types/storage.ts +1 -1
  60. package/backend/src/types/webhooks.ts +45 -0
  61. package/backend/src/utils/cookies.ts +34 -35
  62. package/backend/src/utils/environment.ts +0 -14
  63. package/backend/src/utils/s3-config-loader.ts +64 -64
  64. package/backend/src/utils/seed.ts +334 -301
  65. package/backend/src/utils/sql-parser.ts +126 -0
  66. package/backend/src/utils/utils.ts +114 -114
  67. package/backend/src/utils/validations.ts +10 -10
  68. package/backend/tests/local/test-rpc.sh +141 -0
  69. package/backend/tests/local/test-secrets.sh +1 -1
  70. package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
  71. package/backend/tests/manual/test-rawsql-modes.sh +24 -24
  72. package/backend/tests/unit/database-advance.test.ts +326 -0
  73. package/backend/tests/unit/helpers.test.ts +2 -2
  74. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
  75. package/docker-compose.prod.yml +1 -1
  76. package/docker-compose.yml +1 -1
  77. package/docs/agent-docs/deployment.md +79 -0
  78. package/docs/changelog.mdx +165 -72
  79. package/docs/core-concepts/ai/architecture.mdx +1 -23
  80. package/docs/core-concepts/ai/sdk.mdx +26 -1
  81. package/docs/core-concepts/authentication/architecture.mdx +6 -8
  82. package/docs/core-concepts/authentication/sdk.mdx +387 -91
  83. package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
  84. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
  85. package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
  86. package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
  87. package/docs/core-concepts/database/architecture.mdx +58 -21
  88. package/docs/core-concepts/database/pgvector.mdx +138 -0
  89. package/docs/core-concepts/database/sdk.mdx +17 -17
  90. package/docs/core-concepts/deployments/architecture.mdx +152 -0
  91. package/docs/core-concepts/email/architecture.mdx +4 -2
  92. package/docs/core-concepts/functions/architecture.mdx +1 -1
  93. package/docs/core-concepts/functions/sdk.mdx +0 -1
  94. package/docs/core-concepts/realtime/architecture.mdx +1 -1
  95. package/docs/core-concepts/storage/architecture.mdx +1 -1
  96. package/docs/core-concepts/storage/sdk.mdx +25 -25
  97. package/docs/docs.json +14 -6
  98. package/docs/favicon.png +0 -0
  99. package/docs/favicon.svg +3 -18
  100. package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
  101. package/docs/images/changelog/dec-2025/moreModels.png +0 -0
  102. package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
  103. package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
  104. package/docs/images/changelog/dec-2025/realtime2.png +0 -0
  105. package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
  106. package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
  107. package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
  108. package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
  109. package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
  110. package/docs/images/mcp-setup/claude-code-connect.png +0 -0
  111. package/docs/images/mcp-setup/cline-1.png +0 -0
  112. package/docs/images/mcp-setup/cline-2.png +0 -0
  113. package/docs/images/mcp-setup/cline-3.png +0 -0
  114. package/docs/images/mcp-setup/connect-project.png +0 -0
  115. package/docs/images/mcp-setup/copilot-1.png +0 -0
  116. package/docs/images/mcp-setup/copilot-2.png +0 -0
  117. package/docs/images/mcp-setup/copilot-3.png +0 -0
  118. package/docs/images/mcp-setup/mcp-json-1.png +0 -0
  119. package/docs/images/mcp-setup/mcp-json-2.png +0 -0
  120. package/docs/images/mcp-setup/qoder-1.png +0 -0
  121. package/docs/images/mcp-setup/qoder-2.png +0 -0
  122. package/docs/images/mcp-setup/roocode-1.png +0 -0
  123. package/docs/images/mcp-setup/roocode-2.png +0 -0
  124. package/docs/images/mcp-setup/trae-1.png +0 -0
  125. package/docs/images/mcp-setup/trae-2.png +0 -0
  126. package/docs/images/mcp-setup/trae-3.png +0 -0
  127. package/docs/images/mcp-setup/trae-4.png +0 -0
  128. package/docs/images/mcp-setup/trae-5.png +0 -0
  129. package/docs/images/mcp-setup/windsurf-1.png +0 -0
  130. package/docs/images/mcp-setup/windsurf-2.png +0 -0
  131. package/docs/insforge-instructions-sdk.md +7 -3
  132. package/docs/introduction.mdx +9 -8
  133. package/docs/mcp-setup.mdx +332 -0
  134. package/docs/oauth-server.mdx +563 -0
  135. package/docs/partnership.mdx +79 -10
  136. package/docs/quickstart.mdx +1 -1
  137. package/docs/vscode-extension.mdx +74 -0
  138. package/eslint.config.js +1 -0
  139. package/examples/response-examples.md +1 -1
  140. package/frontend/package.json +1 -1
  141. package/frontend/src/App.tsx +8 -3
  142. package/frontend/src/assets/logos/antigravity.svg +1 -0
  143. package/frontend/src/assets/logos/copilot.svg +10 -0
  144. package/frontend/src/assets/logos/deepseek.svg +139 -0
  145. package/frontend/src/assets/logos/kiro.svg +9 -0
  146. package/frontend/src/assets/logos/qoder.svg +4 -0
  147. package/frontend/src/assets/logos/qwen.svg +15 -0
  148. package/frontend/src/components/CodeBlock.tsx +2 -2
  149. package/frontend/src/components/ConnectCTA.tsx +3 -2
  150. package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
  151. package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
  152. package/frontend/src/components/datagrid/index.ts +1 -1
  153. package/frontend/src/components/index.ts +0 -1
  154. package/frontend/src/components/layout/AppHeader.tsx +4 -27
  155. package/frontend/src/components/layout/AppSidebar.tsx +85 -100
  156. package/frontend/src/components/layout/Layout.tsx +34 -32
  157. package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
  158. package/frontend/src/components/radix/Select.tsx +151 -151
  159. package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
  160. package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
  161. package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
  162. package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
  163. package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
  164. package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
  165. package/frontend/src/features/ai/components/index.ts +6 -6
  166. package/frontend/src/features/ai/helpers.ts +147 -141
  167. package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
  168. package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
  169. package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
  170. package/frontend/src/features/auth/components/index.ts +5 -5
  171. package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
  172. package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
  173. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
  174. package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
  175. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
  176. package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
  177. package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
  178. package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
  179. package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
  180. package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
  181. package/frontend/src/features/database/constants.ts +16 -28
  182. package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
  183. package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
  184. package/frontend/src/features/database/hooks/useTables.ts +5 -7
  185. package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
  186. package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
  187. package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
  188. package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
  189. package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
  190. package/frontend/src/features/database/services/advance.service.ts +1 -15
  191. package/frontend/src/features/database/services/record.service.ts +4 -20
  192. package/frontend/src/features/database/services/table.service.ts +1 -4
  193. package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
  194. package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
  195. package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
  196. package/frontend/src/features/database/templates/notion-clone.ts +8 -8
  197. package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
  198. package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
  199. package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
  200. package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
  201. package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
  202. package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
  203. package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
  204. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
  205. package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
  206. package/frontend/src/features/functions/components/index.ts +5 -5
  207. package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
  208. package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
  209. package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
  210. package/frontend/src/features/functions/services/function.service.ts +8 -25
  211. package/frontend/src/features/functions/services/secret.service.ts +23 -41
  212. package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
  213. package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
  214. package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
  215. package/frontend/src/features/logs/components/index.ts +1 -0
  216. package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
  217. package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
  218. package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
  219. package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
  220. package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
  221. package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
  222. package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
  223. package/frontend/src/features/onboard/components/index.ts +9 -4
  224. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
  225. package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
  226. package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
  227. package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
  228. package/frontend/src/features/onboard/index.ts +17 -13
  229. package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
  230. package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
  231. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
  232. package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
  233. package/frontend/src/index.css +249 -249
  234. package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
  235. package/frontend/src/lib/hooks/useMetadata.ts +45 -1
  236. package/frontend/src/lib/hooks/useModal.tsx +2 -0
  237. package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
  238. package/frontend/src/lib/services/metadata.service.ts +20 -3
  239. package/frontend/src/lib/utils/menuItems.ts +223 -207
  240. package/frontend/src/lib/utils/utils.ts +196 -196
  241. package/functions/server.ts +315 -315
  242. package/functions/worker-template.js +1 -1
  243. package/openapi/ai.yaml +115 -5
  244. package/openapi/auth.yaml +97 -17
  245. package/openapi/logs.yaml +0 -2
  246. package/openapi/metadata.yaml +0 -2
  247. package/openapi/records.yaml +21 -21
  248. package/openapi/tables.yaml +1 -2
  249. package/package.json +1 -1
  250. package/shared-schemas/package.json +1 -1
  251. package/shared-schemas/src/ai-api.schema.ts +251 -143
  252. package/shared-schemas/src/ai.schema.ts +63 -63
  253. package/shared-schemas/src/auth-api.schema.ts +34 -6
  254. package/shared-schemas/src/auth.schema.ts +17 -10
  255. package/shared-schemas/src/cloud-events.schema.ts +26 -0
  256. package/shared-schemas/src/deployments-api.schema.ts +55 -0
  257. package/shared-schemas/src/deployments.schema.ts +30 -0
  258. package/shared-schemas/src/docs.schema.ts +8 -2
  259. package/shared-schemas/src/email-api.schema.ts +30 -30
  260. package/shared-schemas/src/functions-api.schema.ts +13 -4
  261. package/shared-schemas/src/functions.schema.ts +1 -1
  262. package/shared-schemas/src/index.ts +22 -18
  263. package/shared-schemas/src/metadata.schema.ts +30 -4
  264. package/shared-schemas/src/secrets-api.schema.ts +44 -0
  265. package/shared-schemas/src/secrets.schema.ts +15 -0
  266. package/zeabur/README.md +13 -0
  267. package/zeabur/template.yml +20 -51
  268. package/backend/src/types/profile.ts +0 -55
  269. package/frontend/src/components/ProjectInfoModal.tsx +0 -128
@@ -1,200 +1,200 @@
1
- import {
2
- Button,
3
- DropdownMenu,
4
- DropdownMenuContent,
5
- DropdownMenuItem,
6
- DropdownMenuTrigger,
7
- Tooltip,
8
- TooltipTrigger,
9
- TooltipContent,
10
- TooltipProvider,
11
- } from '@/components';
12
-
13
- import { MoreHorizontal, Pencil, Trash2, DollarSign } from 'lucide-react';
14
- import {
15
- // formatTokenCount,
16
- getModalityIcon,
17
- ModelOption,
18
- } from '../helpers';
19
- import { cn } from '@/lib/utils/utils';
20
-
21
- interface AIModelCardProps {
22
- config: ModelOption;
23
- onEdit?: (id: string) => void;
24
- onDelete?: (id: string) => void;
25
- mode?: 'configured' | 'selectable';
26
- isSelected?: boolean;
27
- onSelect?: () => void;
28
- isDisabled?: boolean;
29
- }
30
-
31
- export function AIModelCard({
32
- config,
33
- onEdit,
34
- onDelete,
35
- mode = 'configured',
36
- isSelected = false,
37
- onSelect,
38
- isDisabled = false,
39
- }: AIModelCardProps) {
40
- const isSelectableMode = mode === 'selectable';
41
-
42
- const handleCardClick = () => {
43
- if (isSelectableMode && !isDisabled && onSelect) {
44
- onSelect();
45
- }
46
- };
47
-
48
- return (
49
- <TooltipProvider>
50
- <div
51
- className={cn(
52
- 'relative py-5 px-4 bg-white dark:bg-[#333333] rounded-[8px] transition-all duration-200',
53
- isSelectableMode && !isDisabled && 'cursor-pointer',
54
- isSelectableMode && isSelected
55
- ? 'border-2 border-zinc-700 dark:border-emerald-300'
56
- : 'border-2 border-neutral-200 dark:border-neutral-700',
57
- isSelectableMode &&
58
- !isDisabled &&
59
- !isSelected &&
60
- 'hover:shadow-md hover:bg-neutral-100 dark:hover:bg-neutral-700',
61
- isDisabled && 'opacity-50 cursor-not-allowed'
62
- )}
63
- onClick={handleCardClick}
64
- >
65
- {/* Configured mode: More button */}
66
- {mode === 'configured' && onEdit && onDelete && (
67
- <DropdownMenu>
68
- <DropdownMenuTrigger asChild>
69
- <Button
70
- variant="ghost"
71
- size="icon"
72
- className="h-7 w-7 p-1 hover:bg-neutral-200 dark:hover:bg-neutral-700 absolute top-3 right-3"
73
- >
74
- <MoreHorizontal className="h-4 w-4 text-neutral-500 dark:text-neutral-400" />
75
- </Button>
76
- </DropdownMenuTrigger>
77
- <DropdownMenuContent align="end">
78
- <DropdownMenuItem
79
- className="flex flex-row gap-3 px-3 py-2 cursor-pointer"
80
- onClick={() => onEdit(config.id)}
81
- >
82
- <Pencil className="w-4 h-4" /> System Prompt
83
- </DropdownMenuItem>
84
- <DropdownMenuItem
85
- onClick={() => onDelete(config.id)}
86
- className="text-red-600 dark:text-red-400 flex flex-row gap-3 px-3 py-2 cursor-pointer"
87
- >
88
- <Trash2 className="w-4 h-4" /> Delete
89
- </DropdownMenuItem>
90
- </DropdownMenuContent>
91
- </DropdownMenu>
92
- )}
93
-
94
- {/* Selectable mode: Added badge */}
95
- {isSelectableMode && isDisabled && (
96
- <div className="absolute top-3 right-3 px-2 py-1 text-xs font-medium text-neutral-500 dark:text-neutral-400 bg-neutral-100 dark:bg-neutral-800 rounded">
97
- Added
98
- </div>
99
- )}
100
-
101
- {/* Header with logo */}
102
- <div className="flex items-start justify-between mb-4">
103
- <div className="flex items-center gap-3">
104
- {/* Provider Logo */}
105
- <Tooltip key={config.providerName}>
106
- <TooltipTrigger asChild>
107
- <div className="w-10 h-10">
108
- {config.logo ? (
109
- <config.logo className="w-10 h-10 dark:text-white" />
110
- ) : (
111
- <div className="w-10 h-10 bg-gray-500 rounded flex items-center justify-center text-white text-sm font-bold">
112
- {config.providerName.charAt(0).toUpperCase()}
113
- </div>
114
- )}
115
- </div>
116
- </TooltipTrigger>
117
- <TooltipContent side="top" sideOffset={8}>
118
- <p className="capitalize">{config.providerName}</p>
119
- </TooltipContent>
120
- </Tooltip>
121
-
122
- {/* Model Info */}
123
- <p
124
- className="font-medium text-sm text-zinc-950 dark:text-zinc-50 max-h-10 line-clamp-2"
125
- title={config.modelName ? config.modelName : config.providerName}
126
- >
127
- {config.modelName ? config.modelName : config.providerName}
128
- </p>
129
- {(!isSelectableMode || (isSelectableMode && isDisabled)) && <div className="w-6" />}
130
- </div>
131
- </div>
132
-
133
- <div className="h-px bg-neutral-200 dark:bg-neutral-700 my-3" />
134
-
135
- {/* Modality indicators - use config modalities directly */}
136
- <div className="flex flex-col gap-3 items-stretch">
137
- <div className="flex items-center justify-between">
138
- <span className="text-black dark:text-white">Input</span>
139
- <div className="flex items-center gap-2">
140
- {config.inputModality.map((modality) => {
141
- const IconComponent = getModalityIcon(modality);
142
- return (
143
- <Tooltip key={modality}>
144
- <TooltipTrigger asChild>
145
- <IconComponent className="w-5 h-5 text-neutral-500 hover:text-black dark:text-neutral-400 dark:hover:text-white transition-colors duration-200" />
146
- </TooltipTrigger>
147
- <TooltipContent side="top" sideOffset={8}>
148
- <p className="capitalize">{modality}</p>
149
- </TooltipContent>
150
- </Tooltip>
151
- );
152
- })}
153
- </div>
154
- </div>
155
-
156
- <div className="flex items-center justify-between">
157
- <span className="text-black dark:text-white">Output</span>
158
- <div className="flex items-center gap-2">
159
- {config.outputModality.map((modality) => {
160
- const IconComponent = getModalityIcon(modality);
161
- return (
162
- <Tooltip key={modality}>
163
- <TooltipTrigger asChild>
164
- <IconComponent className="w-5 h-5 text-neutral-500 hover:text-black dark:text-neutral-400 dark:hover:text-white transition-colors duration-200" />
165
- </TooltipTrigger>
166
- <TooltipContent side="top" sideOffset={8}>
167
- <p className="capitalize">{modality}</p>
168
- </TooltipContent>
169
- </Tooltip>
170
- );
171
- })}
172
- </div>
173
- </div>
174
-
175
- {isSelectableMode ? (
176
- <div className="flex items-center justify-between">
177
- <span className="text-black dark:text-white">Credit Usage</span>
178
- <div className="flex items-center">
179
- {typeof config.priceLevel === 'number' && config.priceLevel > 0 ? (
180
- Array.from({ length: config.priceLevel }).map((_, i) => (
181
- <div key={i} className="w-5 h-5 flex items-center justify-center">
182
- <DollarSign className="w-4 h-4 text-neutral-500 dark:text-neutral-400" />
183
- </div>
184
- ))
185
- ) : (
186
- <span className="text-sm text-neutral-500 dark:text-neutral-400">Free</span>
187
- )}
188
- </div>
189
- </div>
190
- ) : (
191
- <div className="flex items-center justify-between text-sm text-black dark:text-white">
192
- <span>Requests</span>
193
- <span>{config.usageStats?.totalRequests || 0}</span>
194
- </div>
195
- )}
196
- </div>
197
- </div>
198
- </TooltipProvider>
199
- );
200
- }
1
+ import {
2
+ Button,
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuItem,
6
+ DropdownMenuTrigger,
7
+ Tooltip,
8
+ TooltipTrigger,
9
+ TooltipContent,
10
+ TooltipProvider,
11
+ } from '@/components';
12
+
13
+ import { MoreHorizontal, Pencil, Trash2, DollarSign } from 'lucide-react';
14
+ import {
15
+ // formatTokenCount,
16
+ getModalityIcon,
17
+ ModelOption,
18
+ } from '../helpers';
19
+ import { cn } from '@/lib/utils/utils';
20
+
21
+ interface AIModelCardProps {
22
+ config: ModelOption;
23
+ onEdit?: (id: string) => void;
24
+ onDelete?: (id: string) => void;
25
+ mode?: 'configured' | 'selectable';
26
+ isSelected?: boolean;
27
+ onSelect?: () => void;
28
+ isDisabled?: boolean;
29
+ }
30
+
31
+ export function AIModelCard({
32
+ config,
33
+ onEdit,
34
+ onDelete,
35
+ mode = 'configured',
36
+ isSelected = false,
37
+ onSelect,
38
+ isDisabled = false,
39
+ }: AIModelCardProps) {
40
+ const isSelectableMode = mode === 'selectable';
41
+
42
+ const handleCardClick = () => {
43
+ if (isSelectableMode && !isDisabled && onSelect) {
44
+ onSelect();
45
+ }
46
+ };
47
+
48
+ return (
49
+ <TooltipProvider>
50
+ <div
51
+ className={cn(
52
+ 'relative py-5 px-4 bg-white dark:bg-[#333333] rounded-[8px] transition-all duration-200',
53
+ isSelectableMode && !isDisabled && 'cursor-pointer',
54
+ isSelectableMode && isSelected
55
+ ? 'border-2 border-zinc-700 dark:border-emerald-300'
56
+ : 'border-2 border-neutral-200 dark:border-neutral-700',
57
+ isSelectableMode &&
58
+ !isDisabled &&
59
+ !isSelected &&
60
+ 'hover:shadow-md hover:bg-neutral-100 dark:hover:bg-neutral-700',
61
+ isDisabled && 'opacity-50 cursor-not-allowed'
62
+ )}
63
+ onClick={handleCardClick}
64
+ >
65
+ {/* Configured mode: More button */}
66
+ {mode === 'configured' && onEdit && onDelete && (
67
+ <DropdownMenu>
68
+ <DropdownMenuTrigger asChild>
69
+ <Button
70
+ variant="ghost"
71
+ size="icon"
72
+ className="h-7 w-7 p-1 hover:bg-neutral-200 dark:hover:bg-neutral-700 absolute top-3 right-3"
73
+ >
74
+ <MoreHorizontal className="h-4 w-4 text-neutral-500 dark:text-neutral-400" />
75
+ </Button>
76
+ </DropdownMenuTrigger>
77
+ <DropdownMenuContent align="end">
78
+ <DropdownMenuItem
79
+ className="flex flex-row gap-3 px-3 py-2 cursor-pointer"
80
+ onClick={() => onEdit(config.id)}
81
+ >
82
+ <Pencil className="w-4 h-4" /> System Prompt
83
+ </DropdownMenuItem>
84
+ <DropdownMenuItem
85
+ onClick={() => onDelete(config.id)}
86
+ className="text-red-600 dark:text-red-400 flex flex-row gap-3 px-3 py-2 cursor-pointer"
87
+ >
88
+ <Trash2 className="w-4 h-4" /> Delete
89
+ </DropdownMenuItem>
90
+ </DropdownMenuContent>
91
+ </DropdownMenu>
92
+ )}
93
+
94
+ {/* Selectable mode: Added badge */}
95
+ {isSelectableMode && isDisabled && (
96
+ <div className="absolute top-3 right-3 px-2 py-1 text-xs font-medium text-neutral-500 dark:text-neutral-400 bg-neutral-100 dark:bg-neutral-800 rounded">
97
+ Added
98
+ </div>
99
+ )}
100
+
101
+ {/* Header with logo */}
102
+ <div className="flex items-start justify-between mb-4">
103
+ <div className="flex items-center gap-3">
104
+ {/* Provider Logo */}
105
+ <Tooltip key={config.providerName}>
106
+ <TooltipTrigger asChild>
107
+ <div className="w-10 h-10">
108
+ {config.logo ? (
109
+ <config.logo className="w-10 h-10 dark:text-white" />
110
+ ) : (
111
+ <div className="w-10 h-10 bg-gray-500 rounded flex items-center justify-center text-white text-sm font-bold">
112
+ {config.providerName.charAt(0).toUpperCase()}
113
+ </div>
114
+ )}
115
+ </div>
116
+ </TooltipTrigger>
117
+ <TooltipContent side="top" sideOffset={8}>
118
+ <p className="capitalize">{config.providerName}</p>
119
+ </TooltipContent>
120
+ </Tooltip>
121
+
122
+ {/* Model Info */}
123
+ <p
124
+ className="font-medium text-sm text-zinc-950 dark:text-zinc-50 max-h-10 line-clamp-2"
125
+ title={config.modelName ? config.modelName : config.providerName}
126
+ >
127
+ {config.modelName ? config.modelName : config.providerName}
128
+ </p>
129
+ {(!isSelectableMode || (isSelectableMode && isDisabled)) && <div className="w-6" />}
130
+ </div>
131
+ </div>
132
+
133
+ <div className="h-px bg-neutral-200 dark:bg-neutral-700 my-3" />
134
+
135
+ {/* Modality indicators - use config modalities directly */}
136
+ <div className="flex flex-col gap-3 items-stretch">
137
+ <div className="flex items-center justify-between">
138
+ <span className="text-black dark:text-white">Input</span>
139
+ <div className="flex items-center gap-2">
140
+ {config.inputModality.map((modality) => {
141
+ const IconComponent = getModalityIcon(modality);
142
+ return (
143
+ <Tooltip key={modality}>
144
+ <TooltipTrigger asChild>
145
+ <IconComponent className="w-5 h-5 text-neutral-500 hover:text-black dark:text-neutral-400 dark:hover:text-white transition-colors duration-200" />
146
+ </TooltipTrigger>
147
+ <TooltipContent side="top" sideOffset={8}>
148
+ <p className="capitalize">{modality}</p>
149
+ </TooltipContent>
150
+ </Tooltip>
151
+ );
152
+ })}
153
+ </div>
154
+ </div>
155
+
156
+ <div className="flex items-center justify-between">
157
+ <span className="text-black dark:text-white">Output</span>
158
+ <div className="flex items-center gap-2">
159
+ {config.outputModality.map((modality) => {
160
+ const IconComponent = getModalityIcon(modality);
161
+ return (
162
+ <Tooltip key={modality}>
163
+ <TooltipTrigger asChild>
164
+ <IconComponent className="w-5 h-5 text-neutral-500 hover:text-black dark:text-neutral-400 dark:hover:text-white transition-colors duration-200" />
165
+ </TooltipTrigger>
166
+ <TooltipContent side="top" sideOffset={8}>
167
+ <p className="capitalize">{modality}</p>
168
+ </TooltipContent>
169
+ </Tooltip>
170
+ );
171
+ })}
172
+ </div>
173
+ </div>
174
+
175
+ {isSelectableMode ? (
176
+ <div className="flex items-center justify-between">
177
+ <span className="text-black dark:text-white">Credit Usage</span>
178
+ <div className="flex items-center">
179
+ {typeof config.priceLevel === 'number' && config.priceLevel > 0 ? (
180
+ Array.from({ length: config.priceLevel }).map((_, i) => (
181
+ <div key={i} className="w-5 h-5 flex items-center justify-center">
182
+ <DollarSign className="w-4 h-4 text-neutral-500 dark:text-neutral-400" />
183
+ </div>
184
+ ))
185
+ ) : (
186
+ <span className="text-sm text-neutral-500 dark:text-neutral-400">Free</span>
187
+ )}
188
+ </div>
189
+ </div>
190
+ ) : (
191
+ <div className="flex items-center justify-between text-sm text-black dark:text-white">
192
+ <span>Requests</span>
193
+ <span>{config.usageStats?.totalRequests || 0}</span>
194
+ </div>
195
+ )}
196
+ </div>
197
+ </div>
198
+ </TooltipProvider>
199
+ );
200
+ }
@@ -1,23 +1,23 @@
1
- import React from 'react';
2
- import { Sparkles } from 'lucide-react';
3
-
4
- interface AIEmptyStateProps {
5
- title: string;
6
- description?: string;
7
- }
8
-
9
- const AIEmptyState: React.FC<AIEmptyStateProps> = ({ title, description }) => {
10
- return (
11
- <div className="flex flex-col items-center justify-center py-8 text-center gap-3 rounded-[8px] bg-neutral-100 dark:bg-[#333333]">
12
- <Sparkles size={40} className="text-neutral-400 dark:text-neutral-600" />
13
- <div className="flex flex-col items-center justify-center gap-1">
14
- <p className="text-sm font-medium text-zinc-950 dark:text-white">{title}</p>
15
- {description && (
16
- <p className="text-neutral-500 dark:text-neutral-400 text-xs max-w-md">{description}</p>
17
- )}
18
- </div>
19
- </div>
20
- );
21
- };
22
-
23
- export default AIEmptyState;
1
+ import React from 'react';
2
+ import { Sparkles } from 'lucide-react';
3
+
4
+ interface AIEmptyStateProps {
5
+ title: string;
6
+ description?: string;
7
+ }
8
+
9
+ const AIEmptyState: React.FC<AIEmptyStateProps> = ({ title, description }) => {
10
+ return (
11
+ <div className="flex flex-col items-center justify-center py-8 text-center gap-3 rounded-[8px] bg-neutral-100 dark:bg-[#333333]">
12
+ <Sparkles size={40} className="text-neutral-400 dark:text-neutral-600" />
13
+ <div className="flex flex-col items-center justify-center gap-1">
14
+ <p className="text-sm font-medium text-zinc-950 dark:text-white">{title}</p>
15
+ {description && (
16
+ <p className="text-neutral-500 dark:text-neutral-400 text-xs max-w-md">{description}</p>
17
+ )}
18
+ </div>
19
+ </div>
20
+ );
21
+ };
22
+
23
+ export default AIEmptyState;
@@ -1,101 +1,102 @@
1
- import { Checkbox, Label } from '@/components';
2
- import { ModalitySchema } from '@insforge/shared-schemas';
3
-
4
- interface ModalityFilterSidebarProps {
5
- inputModalities: ModalitySchema[];
6
- outputModalities: ModalitySchema[];
7
- onInputChange: (modalities: ModalitySchema[]) => void;
8
- onOutputChange: (modalities: ModalitySchema[]) => void;
9
- }
10
-
11
- const modalityOptions: Array<{
12
- value: ModalitySchema;
13
- label: string;
14
- }> = [
15
- { value: 'text', label: 'Text' },
16
- { value: 'image', label: 'Image' },
17
- ];
18
-
19
- export function ModalityFilterSidebar({
20
- inputModalities,
21
- outputModalities,
22
- onInputChange,
23
- onOutputChange,
24
- }: ModalityFilterSidebarProps) {
25
- const handleModalityToggle = (
26
- type: 'input' | 'output',
27
- modality: ModalitySchema,
28
- checked: boolean
29
- ) => {
30
- const currentModalities = type === 'input' ? inputModalities : outputModalities;
31
- const setModalities = type === 'input' ? onInputChange : onOutputChange;
32
-
33
- if (checked) {
34
- setModalities([...currentModalities, modality]);
35
- } else {
36
- setModalities(currentModalities.filter((m) => m !== modality));
37
- }
38
- };
39
-
40
- return (
41
- <div className="w-30 flex-shrink-0 flex flex-col gap-6">
42
- <p className="text-base font-medium text-zinc-950 dark:text-neutral-50">Filters</p>
43
-
44
- {/* Input Section */}
45
- <div className="flex flex-col gap-3">
46
- <Label className="text-sm font-normal text-zinc-950 dark:text-neutral-50 block">
47
- Input
48
- </Label>
49
- <div className="flex flex-col gap-4">
50
- {modalityOptions.map((option) => {
51
- const isChecked = inputModalities.includes(option.value);
52
- return (
53
- <div key={option.value} className="flex items-center gap-x-2">
54
- <Checkbox
55
- checked={isChecked}
56
- onChange={(checked: boolean) =>
57
- handleModalityToggle('input', option.value, checked)
58
- }
59
- />
60
- <Label
61
- className="text-sm font-normal text-zinc-950 dark:text-white cursor-pointer"
62
- onClick={() => handleModalityToggle('input', option.value, !isChecked)}
63
- >
64
- {option.label}
65
- </Label>
66
- </div>
67
- );
68
- })}
69
- </div>
70
- </div>
71
-
72
- {/* Output Section */}
73
- <div className="flex flex-col gap-3">
74
- <Label className="text-sm font-normal text-zinc-950 dark:text-neutral-50 block">
75
- Output
76
- </Label>
77
- <div className="flex flex-col gap-4">
78
- {modalityOptions.map((option) => {
79
- const isChecked = outputModalities.includes(option.value);
80
- return (
81
- <div key={option.value} className="flex items-center gap-x-2">
82
- <Checkbox
83
- checked={isChecked}
84
- onChange={(checked: boolean) =>
85
- handleModalityToggle('output', option.value, checked)
86
- }
87
- />
88
- <Label
89
- className="text-sm font-normal text-zinc-950 dark:text-white cursor-pointer"
90
- onClick={() => handleModalityToggle('output', option.value, !isChecked)}
91
- >
92
- {option.label}
93
- </Label>
94
- </div>
95
- );
96
- })}
97
- </div>
98
- </div>
99
- </div>
100
- );
101
- }
1
+ import { Checkbox, Label } from '@/components';
2
+ import { ModalitySchema } from '@insforge/shared-schemas';
3
+
4
+ interface ModalityFilterSidebarProps {
5
+ inputModalities: ModalitySchema[];
6
+ outputModalities: ModalitySchema[];
7
+ onInputChange: (modalities: ModalitySchema[]) => void;
8
+ onOutputChange: (modalities: ModalitySchema[]) => void;
9
+ }
10
+
11
+ const modalityOptions: Array<{
12
+ value: ModalitySchema;
13
+ label: string;
14
+ }> = [
15
+ { value: 'text', label: 'Text' },
16
+ { value: 'image', label: 'Image' },
17
+ { value: 'audio', label: 'Audio' },
18
+ ];
19
+
20
+ export function ModalityFilterSidebar({
21
+ inputModalities,
22
+ outputModalities,
23
+ onInputChange,
24
+ onOutputChange,
25
+ }: ModalityFilterSidebarProps) {
26
+ const handleModalityToggle = (
27
+ type: 'input' | 'output',
28
+ modality: ModalitySchema,
29
+ checked: boolean
30
+ ) => {
31
+ const currentModalities = type === 'input' ? inputModalities : outputModalities;
32
+ const setModalities = type === 'input' ? onInputChange : onOutputChange;
33
+
34
+ if (checked) {
35
+ setModalities([...currentModalities, modality]);
36
+ } else {
37
+ setModalities(currentModalities.filter((m) => m !== modality));
38
+ }
39
+ };
40
+
41
+ return (
42
+ <div className="w-30 flex-shrink-0 flex flex-col gap-6">
43
+ <p className="text-base font-medium text-zinc-950 dark:text-neutral-50">Filters</p>
44
+
45
+ {/* Input Section */}
46
+ <div className="flex flex-col gap-3">
47
+ <Label className="text-sm font-normal text-zinc-950 dark:text-neutral-50 block">
48
+ Input
49
+ </Label>
50
+ <div className="flex flex-col gap-4">
51
+ {modalityOptions.map((option) => {
52
+ const isChecked = inputModalities.includes(option.value);
53
+ return (
54
+ <div key={option.value} className="flex items-center gap-x-2">
55
+ <Checkbox
56
+ checked={isChecked}
57
+ onChange={(checked: boolean) =>
58
+ handleModalityToggle('input', option.value, checked)
59
+ }
60
+ />
61
+ <Label
62
+ className="text-sm font-normal text-zinc-950 dark:text-white cursor-pointer"
63
+ onClick={() => handleModalityToggle('input', option.value, !isChecked)}
64
+ >
65
+ {option.label}
66
+ </Label>
67
+ </div>
68
+ );
69
+ })}
70
+ </div>
71
+ </div>
72
+
73
+ {/* Output Section */}
74
+ <div className="flex flex-col gap-3">
75
+ <Label className="text-sm font-normal text-zinc-950 dark:text-neutral-50 block">
76
+ Output
77
+ </Label>
78
+ <div className="flex flex-col gap-4">
79
+ {modalityOptions.map((option) => {
80
+ const isChecked = outputModalities.includes(option.value);
81
+ return (
82
+ <div key={option.value} className="flex items-center gap-x-2">
83
+ <Checkbox
84
+ checked={isChecked}
85
+ onChange={(checked: boolean) =>
86
+ handleModalityToggle('output', option.value, checked)
87
+ }
88
+ />
89
+ <Label
90
+ className="text-sm font-normal text-zinc-950 dark:text-white cursor-pointer"
91
+ onClick={() => handleModalityToggle('output', option.value, !isChecked)}
92
+ >
93
+ {option.label}
94
+ </Label>
95
+ </div>
96
+ );
97
+ })}
98
+ </div>
99
+ </div>
100
+ </div>
101
+ );
102
+ }