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.
- package/CHANGELOG.md +2 -0
- package/auth/package.json +5 -3
- package/auth/src/lib/broadcastService.ts +115 -117
- package/auth/src/lib/insforge.ts +8 -0
- package/auth/src/main.tsx +2 -4
- package/auth/src/pages/SignInPage.tsx +60 -60
- package/auth/src/pages/SignUpPage.tsx +60 -60
- package/auth/src/pages/VerifyEmailPage.tsx +18 -0
- package/auth/tsconfig.json +2 -1
- package/backend/package.json +10 -6
- package/backend/src/api/middlewares/rate-limiters.ts +127 -127
- package/backend/src/api/routes/ai/index.routes.ts +475 -468
- package/backend/src/api/routes/auth/index.routes.ts +85 -32
- package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
- package/backend/src/api/routes/database/index.routes.ts +2 -0
- package/backend/src/api/routes/database/records.routes.ts +39 -175
- package/backend/src/api/routes/database/rpc.routes.ts +69 -0
- package/backend/src/api/routes/deployments/index.routes.ts +192 -0
- package/backend/src/api/routes/docs/index.routes.ts +3 -2
- package/backend/src/api/routes/email/index.routes.ts +35 -35
- package/backend/src/api/routes/functions/index.routes.ts +3 -3
- package/backend/src/api/routes/metadata/index.routes.ts +26 -0
- package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
- package/backend/src/infra/database/database.manager.ts +0 -10
- package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
- package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
- package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
- package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
- package/backend/src/infra/security/token.manager.ts +1 -4
- package/backend/src/providers/ai/openrouter.provider.ts +12 -3
- package/backend/src/providers/database/base.provider.ts +39 -0
- package/backend/src/providers/database/cloud.provider.ts +159 -0
- package/backend/src/providers/deployments/vercel.provider.ts +516 -0
- package/backend/src/server.ts +19 -7
- package/backend/src/services/ai/ai-config.service.ts +6 -6
- package/backend/src/services/ai/ai-model.service.ts +60 -60
- package/backend/src/services/ai/ai-usage.service.ts +7 -7
- package/backend/src/services/ai/chat-completion.service.ts +415 -220
- package/backend/src/services/ai/helpers.ts +64 -64
- package/backend/src/services/ai/index.ts +13 -13
- package/backend/src/services/auth/auth-config.service.ts +4 -4
- package/backend/src/services/auth/auth-otp.service.ts +6 -6
- package/backend/src/services/auth/auth.service.ts +134 -74
- package/backend/src/services/auth/index.ts +4 -4
- package/backend/src/services/auth/oauth-config.service.ts +12 -12
- package/backend/src/services/database/database-advance.service.ts +19 -55
- package/backend/src/services/database/database-table.service.ts +38 -85
- package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
- package/backend/src/services/deployments/deployment.service.ts +693 -0
- package/backend/src/services/functions/function.service.ts +61 -41
- package/backend/src/services/logs/audit.service.ts +10 -10
- package/backend/src/services/secrets/secret.service.ts +101 -27
- package/backend/src/services/storage/storage.service.ts +30 -30
- package/backend/src/services/usage/usage.service.ts +6 -6
- package/backend/src/types/ai.ts +8 -0
- package/backend/src/types/auth.ts +5 -1
- package/backend/src/types/database.ts +2 -0
- package/backend/src/types/deployments.ts +33 -0
- package/backend/src/types/storage.ts +1 -1
- package/backend/src/types/webhooks.ts +45 -0
- package/backend/src/utils/cookies.ts +34 -35
- package/backend/src/utils/environment.ts +0 -14
- package/backend/src/utils/s3-config-loader.ts +64 -64
- package/backend/src/utils/seed.ts +334 -301
- package/backend/src/utils/sql-parser.ts +126 -0
- package/backend/src/utils/utils.ts +114 -114
- package/backend/src/utils/validations.ts +10 -10
- package/backend/tests/local/test-rpc.sh +141 -0
- package/backend/tests/local/test-secrets.sh +1 -1
- package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
- package/backend/tests/manual/test-rawsql-modes.sh +24 -24
- package/backend/tests/unit/database-advance.test.ts +326 -0
- package/backend/tests/unit/helpers.test.ts +2 -2
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
- package/docker-compose.prod.yml +1 -1
- package/docker-compose.yml +1 -1
- package/docs/agent-docs/deployment.md +79 -0
- package/docs/changelog.mdx +165 -72
- package/docs/core-concepts/ai/architecture.mdx +1 -23
- package/docs/core-concepts/ai/sdk.mdx +26 -1
- package/docs/core-concepts/authentication/architecture.mdx +6 -8
- package/docs/core-concepts/authentication/sdk.mdx +387 -91
- package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
- package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
- package/docs/core-concepts/database/architecture.mdx +58 -21
- package/docs/core-concepts/database/pgvector.mdx +138 -0
- package/docs/core-concepts/database/sdk.mdx +17 -17
- package/docs/core-concepts/deployments/architecture.mdx +152 -0
- package/docs/core-concepts/email/architecture.mdx +4 -2
- package/docs/core-concepts/functions/architecture.mdx +1 -1
- package/docs/core-concepts/functions/sdk.mdx +0 -1
- package/docs/core-concepts/realtime/architecture.mdx +1 -1
- package/docs/core-concepts/storage/architecture.mdx +1 -1
- package/docs/core-concepts/storage/sdk.mdx +25 -25
- package/docs/docs.json +14 -6
- package/docs/favicon.png +0 -0
- package/docs/favicon.svg +3 -18
- package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
- package/docs/images/changelog/dec-2025/moreModels.png +0 -0
- package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
- package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
- package/docs/images/changelog/dec-2025/realtime2.png +0 -0
- package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
- package/docs/images/mcp-setup/claude-code-connect.png +0 -0
- package/docs/images/mcp-setup/cline-1.png +0 -0
- package/docs/images/mcp-setup/cline-2.png +0 -0
- package/docs/images/mcp-setup/cline-3.png +0 -0
- package/docs/images/mcp-setup/connect-project.png +0 -0
- package/docs/images/mcp-setup/copilot-1.png +0 -0
- package/docs/images/mcp-setup/copilot-2.png +0 -0
- package/docs/images/mcp-setup/copilot-3.png +0 -0
- package/docs/images/mcp-setup/mcp-json-1.png +0 -0
- package/docs/images/mcp-setup/mcp-json-2.png +0 -0
- package/docs/images/mcp-setup/qoder-1.png +0 -0
- package/docs/images/mcp-setup/qoder-2.png +0 -0
- package/docs/images/mcp-setup/roocode-1.png +0 -0
- package/docs/images/mcp-setup/roocode-2.png +0 -0
- package/docs/images/mcp-setup/trae-1.png +0 -0
- package/docs/images/mcp-setup/trae-2.png +0 -0
- package/docs/images/mcp-setup/trae-3.png +0 -0
- package/docs/images/mcp-setup/trae-4.png +0 -0
- package/docs/images/mcp-setup/trae-5.png +0 -0
- package/docs/images/mcp-setup/windsurf-1.png +0 -0
- package/docs/images/mcp-setup/windsurf-2.png +0 -0
- package/docs/insforge-instructions-sdk.md +7 -3
- package/docs/introduction.mdx +9 -8
- package/docs/mcp-setup.mdx +332 -0
- package/docs/oauth-server.mdx +563 -0
- package/docs/partnership.mdx +79 -10
- package/docs/quickstart.mdx +1 -1
- package/docs/vscode-extension.mdx +74 -0
- package/eslint.config.js +1 -0
- package/examples/response-examples.md +1 -1
- package/frontend/package.json +1 -1
- package/frontend/src/App.tsx +8 -3
- package/frontend/src/assets/logos/antigravity.svg +1 -0
- package/frontend/src/assets/logos/copilot.svg +10 -0
- package/frontend/src/assets/logos/deepseek.svg +139 -0
- package/frontend/src/assets/logos/kiro.svg +9 -0
- package/frontend/src/assets/logos/qoder.svg +4 -0
- package/frontend/src/assets/logos/qwen.svg +15 -0
- package/frontend/src/components/CodeBlock.tsx +2 -2
- package/frontend/src/components/ConnectCTA.tsx +3 -2
- package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
- package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
- package/frontend/src/components/datagrid/index.ts +1 -1
- package/frontend/src/components/index.ts +0 -1
- package/frontend/src/components/layout/AppHeader.tsx +4 -27
- package/frontend/src/components/layout/AppSidebar.tsx +85 -100
- package/frontend/src/components/layout/Layout.tsx +34 -32
- package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
- package/frontend/src/components/radix/Select.tsx +151 -151
- package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
- package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
- package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
- package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
- package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
- package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
- package/frontend/src/features/ai/components/index.ts +6 -6
- package/frontend/src/features/ai/helpers.ts +147 -141
- package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
- package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
- package/frontend/src/features/auth/components/index.ts +5 -5
- package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
- package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
- package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
- package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
- package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
- package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
- package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
- package/frontend/src/features/database/constants.ts +16 -28
- package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
- package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
- package/frontend/src/features/database/hooks/useTables.ts +5 -7
- package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
- package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
- package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
- package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
- package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
- package/frontend/src/features/database/services/advance.service.ts +1 -15
- package/frontend/src/features/database/services/record.service.ts +4 -20
- package/frontend/src/features/database/services/table.service.ts +1 -4
- package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
- package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
- package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
- package/frontend/src/features/database/templates/notion-clone.ts +8 -8
- package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
- package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
- package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
- package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
- package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
- package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
- package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
- package/frontend/src/features/functions/components/index.ts +5 -5
- package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
- package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
- package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
- package/frontend/src/features/functions/services/function.service.ts +8 -25
- package/frontend/src/features/functions/services/secret.service.ts +23 -41
- package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
- package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
- package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
- package/frontend/src/features/logs/components/index.ts +1 -0
- package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
- package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
- package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
- package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
- package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
- package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
- package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
- package/frontend/src/features/onboard/components/index.ts +9 -4
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
- package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
- package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
- package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
- package/frontend/src/features/onboard/index.ts +17 -13
- package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
- package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
- package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
- package/frontend/src/index.css +249 -249
- package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
- package/frontend/src/lib/hooks/useMetadata.ts +45 -1
- package/frontend/src/lib/hooks/useModal.tsx +2 -0
- package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
- package/frontend/src/lib/services/metadata.service.ts +20 -3
- package/frontend/src/lib/utils/menuItems.ts +223 -207
- package/frontend/src/lib/utils/utils.ts +196 -196
- package/functions/server.ts +315 -315
- package/functions/worker-template.js +1 -1
- package/openapi/ai.yaml +115 -5
- package/openapi/auth.yaml +97 -17
- package/openapi/logs.yaml +0 -2
- package/openapi/metadata.yaml +0 -2
- package/openapi/records.yaml +21 -21
- package/openapi/tables.yaml +1 -2
- package/package.json +1 -1
- package/shared-schemas/package.json +1 -1
- package/shared-schemas/src/ai-api.schema.ts +251 -143
- package/shared-schemas/src/ai.schema.ts +63 -63
- package/shared-schemas/src/auth-api.schema.ts +34 -6
- package/shared-schemas/src/auth.schema.ts +17 -10
- package/shared-schemas/src/cloud-events.schema.ts +26 -0
- package/shared-schemas/src/deployments-api.schema.ts +55 -0
- package/shared-schemas/src/deployments.schema.ts +30 -0
- package/shared-schemas/src/docs.schema.ts +8 -2
- package/shared-schemas/src/email-api.schema.ts +30 -30
- package/shared-schemas/src/functions-api.schema.ts +13 -4
- package/shared-schemas/src/functions.schema.ts +1 -1
- package/shared-schemas/src/index.ts +22 -18
- package/shared-schemas/src/metadata.schema.ts +30 -4
- package/shared-schemas/src/secrets-api.schema.ts +44 -0
- package/shared-schemas/src/secrets.schema.ts +15 -0
- package/zeabur/README.md +13 -0
- package/zeabur/template.yml +20 -51
- package/backend/src/types/profile.ts +0 -55
- 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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
+
}
|