@geenius/ai 0.1.0 → 0.3.1
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/package.json +15 -2
- package/packages/convex/dist/index.d.ts +1 -0
- package/packages/convex/dist/index.js +42 -0
- package/packages/convex/dist/index.js.map +1 -0
- package/packages/react/README.md +1 -1
- package/packages/react-css/README.md +1 -1
- package/packages/react-css/dist/index.cjs +1544 -0
- package/packages/react-css/dist/index.cjs.map +1 -0
- package/packages/react-css/dist/index.d.cts +454 -0
- package/packages/react-css/dist/index.d.ts +454 -0
- package/packages/react-css/dist/index.js +1495 -0
- package/packages/react-css/dist/index.js.map +1 -0
- package/packages/shared/README.md +1 -1
- package/packages/solidjs/README.md +1 -1
- package/packages/solidjs-css/README.md +1 -1
- package/packages/solidjs-css/dist/index.cjs +674 -0
- package/packages/solidjs-css/dist/index.cjs.map +1 -0
- package/packages/solidjs-css/dist/index.d.cts +254 -0
- package/packages/solidjs-css/dist/index.d.ts +254 -0
- package/packages/solidjs-css/dist/index.js +634 -0
- package/packages/solidjs-css/dist/index.js.map +1 -0
- package/.changeset/config.json +0 -11
- package/.env.example +0 -2
- package/.github/CODEOWNERS +0 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/ci.yml +0 -23
- package/.github/workflows/release.yml +0 -29
- package/.node-version +0 -1
- package/.nvmrc +0 -1
- package/.prettierrc +0 -7
- package/.project/ACCOUNT.yaml +0 -4
- package/.project/IDEAS.yaml +0 -7
- package/.project/PROJECT.yaml +0 -11
- package/.project/ROADMAP.yaml +0 -15
- package/CODE_OF_CONDUCT.md +0 -26
- package/CONTRIBUTING.md +0 -61
- package/SECURITY.md +0 -18
- package/SUPPORT.md +0 -14
- package/packages/convex/package.json +0 -42
- package/packages/convex/src/index.ts +0 -8
- package/packages/convex/src/mutations/messages.ts +0 -29
- package/packages/convex/src/queries/messages.ts +0 -24
- package/packages/convex/src/schema.ts +0 -20
- package/packages/convex/tsconfig.json +0 -11
- package/packages/convex/tsup.config.ts +0 -17
- package/packages/react/package.json +0 -60
- package/packages/react/src/components/AILogTable.tsx +0 -90
- package/packages/react/src/components/ChatWindow.tsx +0 -118
- package/packages/react/src/components/GenerationCard.tsx +0 -73
- package/packages/react/src/components/ImageGenerator.tsx +0 -103
- package/packages/react/src/components/ModelSelector.tsx +0 -44
- package/packages/react/src/components/ModelTestRunner.tsx +0 -148
- package/packages/react/src/components/VoiceSelector.tsx +0 -51
- package/packages/react/src/components/index.ts +0 -9
- package/packages/react/src/hooks/index.ts +0 -12
- package/packages/react/src/hooks/useAI.ts +0 -158
- package/packages/react/src/hooks/useAILogs.ts +0 -40
- package/packages/react/src/hooks/useAIModels.ts +0 -53
- package/packages/react/src/hooks/useChat.ts +0 -141
- package/packages/react/src/hooks/useContentManager.ts +0 -108
- package/packages/react/src/hooks/useImageGeneration.ts +0 -82
- package/packages/react/src/hooks/useMemory.ts +0 -161
- package/packages/react/src/hooks/useModelTest.ts +0 -126
- package/packages/react/src/hooks/useRealtimeAudio.ts +0 -203
- package/packages/react/src/hooks/useSkills.ts +0 -114
- package/packages/react/src/hooks/useTextToSpeech.ts +0 -99
- package/packages/react/src/hooks/useTranscription.ts +0 -119
- package/packages/react/src/hooks/useVideoGeneration.ts +0 -79
- package/packages/react/src/index.ts +0 -42
- package/packages/react/src/pages/AILogsPage.tsx +0 -98
- package/packages/react/src/pages/ChatPage.tsx +0 -42
- package/packages/react/src/pages/ModelTestPage.tsx +0 -33
- package/packages/react/src/pages/index.ts +0 -5
- package/packages/react/tsconfig.json +0 -26
- package/packages/react/tsup.config.ts +0 -22
- package/packages/react-css/package.json +0 -45
- package/packages/react-css/src/ai.css +0 -857
- package/packages/react-css/src/components/AILogTable.tsx +0 -90
- package/packages/react-css/src/components/ChatWindow.tsx +0 -118
- package/packages/react-css/src/components/GenerationCard.tsx +0 -73
- package/packages/react-css/src/components/ImageGenerator.tsx +0 -103
- package/packages/react-css/src/components/ModelSelector.tsx +0 -44
- package/packages/react-css/src/components/ModelTestRunner.tsx +0 -148
- package/packages/react-css/src/components/VoiceSelector.tsx +0 -51
- package/packages/react-css/src/components/index.ts +0 -9
- package/packages/react-css/src/hooks/index.ts +0 -12
- package/packages/react-css/src/hooks/useAI.ts +0 -153
- package/packages/react-css/src/hooks/useAILogs.ts +0 -40
- package/packages/react-css/src/hooks/useAIModels.ts +0 -51
- package/packages/react-css/src/hooks/useChat.ts +0 -145
- package/packages/react-css/src/hooks/useContentManager.ts +0 -108
- package/packages/react-css/src/hooks/useImageGeneration.ts +0 -82
- package/packages/react-css/src/hooks/useMemory.ts +0 -161
- package/packages/react-css/src/hooks/useModelTest.ts +0 -122
- package/packages/react-css/src/hooks/useRealtimeAudio.ts +0 -203
- package/packages/react-css/src/hooks/useSkills.ts +0 -114
- package/packages/react-css/src/hooks/useTextToSpeech.ts +0 -99
- package/packages/react-css/src/hooks/useTranscription.ts +0 -119
- package/packages/react-css/src/hooks/useVideoGeneration.ts +0 -79
- package/packages/react-css/src/index.ts +0 -35
- package/packages/react-css/src/pages/AILogsPage.tsx +0 -98
- package/packages/react-css/src/pages/ChatPage.tsx +0 -42
- package/packages/react-css/src/pages/ModelTestPage.tsx +0 -33
- package/packages/react-css/src/pages/index.ts +0 -5
- package/packages/react-css/src/styles.css +0 -127
- package/packages/react-css/tsconfig.json +0 -26
- package/packages/react-css/tsup.config.ts +0 -2
- package/packages/shared/package.json +0 -71
- package/packages/shared/src/__tests__/ai.test.ts +0 -67
- package/packages/shared/src/ai-client.ts +0 -243
- package/packages/shared/src/config.ts +0 -235
- package/packages/shared/src/content.ts +0 -249
- package/packages/shared/src/convex/helpers.ts +0 -163
- package/packages/shared/src/convex/index.ts +0 -16
- package/packages/shared/src/convex/schemas.ts +0 -146
- package/packages/shared/src/convex/validators.ts +0 -136
- package/packages/shared/src/index.ts +0 -107
- package/packages/shared/src/memory.ts +0 -197
- package/packages/shared/src/providers/base.ts +0 -103
- package/packages/shared/src/providers/elevenlabs.ts +0 -155
- package/packages/shared/src/providers/index.ts +0 -28
- package/packages/shared/src/providers/openai-compatible.ts +0 -286
- package/packages/shared/src/providers/registry.ts +0 -113
- package/packages/shared/src/providers/replicate-fal.ts +0 -230
- package/packages/shared/src/skills.ts +0 -273
- package/packages/shared/src/types.ts +0 -501
- package/packages/shared/tsconfig.json +0 -25
- package/packages/shared/tsup.config.ts +0 -22
- package/packages/shared/vitest.config.ts +0 -4
- package/packages/solidjs/package.json +0 -59
- package/packages/solidjs/src/components/ChatWindow.tsx +0 -78
- package/packages/solidjs/src/components/GenerationCard.tsx +0 -62
- package/packages/solidjs/src/components/ModelTestRunner.tsx +0 -119
- package/packages/solidjs/src/components/index.ts +0 -5
- package/packages/solidjs/src/index.ts +0 -32
- package/packages/solidjs/src/pages/ChatPage.tsx +0 -22
- package/packages/solidjs/src/pages/ModelTestPage.tsx +0 -22
- package/packages/solidjs/src/pages/index.ts +0 -4
- package/packages/solidjs/src/primitives/createAI.ts +0 -79
- package/packages/solidjs/src/primitives/createChat.ts +0 -100
- package/packages/solidjs/src/primitives/createContentManager.ts +0 -61
- package/packages/solidjs/src/primitives/createImageGeneration.ts +0 -46
- package/packages/solidjs/src/primitives/createMemory.ts +0 -127
- package/packages/solidjs/src/primitives/createModelTest.ts +0 -89
- package/packages/solidjs/src/primitives/createSkills.ts +0 -83
- package/packages/solidjs/src/primitives/createTextToSpeech.ts +0 -56
- package/packages/solidjs/src/primitives/createVideoGeneration.ts +0 -46
- package/packages/solidjs/src/primitives/index.ts +0 -8
- package/packages/solidjs/tsconfig.json +0 -27
- package/packages/solidjs/tsup.config.ts +0 -21
- package/packages/solidjs-css/package.json +0 -44
- package/packages/solidjs-css/src/ai.css +0 -857
- package/packages/solidjs-css/src/components/ChatWindow.tsx +0 -78
- package/packages/solidjs-css/src/components/GenerationCard.tsx +0 -62
- package/packages/solidjs-css/src/components/ModelTestRunner.tsx +0 -119
- package/packages/solidjs-css/src/components/index.ts +0 -5
- package/packages/solidjs-css/src/index.ts +0 -26
- package/packages/solidjs-css/src/pages/ChatPage.tsx +0 -22
- package/packages/solidjs-css/src/pages/ModelTestPage.tsx +0 -22
- package/packages/solidjs-css/src/pages/index.ts +0 -4
- package/packages/solidjs-css/src/primitives/createAI.ts +0 -79
- package/packages/solidjs-css/src/primitives/createChat.ts +0 -100
- package/packages/solidjs-css/src/primitives/createContentManager.ts +0 -61
- package/packages/solidjs-css/src/primitives/createImageGeneration.ts +0 -46
- package/packages/solidjs-css/src/primitives/createMemory.ts +0 -127
- package/packages/solidjs-css/src/primitives/createModelTest.ts +0 -89
- package/packages/solidjs-css/src/primitives/createSkills.ts +0 -83
- package/packages/solidjs-css/src/primitives/createTextToSpeech.ts +0 -56
- package/packages/solidjs-css/src/primitives/createVideoGeneration.ts +0 -46
- package/packages/solidjs-css/src/primitives/index.ts +0 -1
- package/packages/solidjs-css/src/styles.css +0 -127
- package/packages/solidjs-css/tsconfig.json +0 -27
- package/packages/solidjs-css/tsup.config.ts +0 -2
- package/pnpm-workspace.yaml +0 -2
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@geenius-ai/react",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": false,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"description": "React hooks, components, and pages for geenius-ai",
|
|
7
|
-
"author": "Antigravity HQ",
|
|
8
|
-
"license": "MIT",
|
|
9
|
-
"main": "./dist/index.js",
|
|
10
|
-
"module": "./dist/index.js",
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"exports": {
|
|
13
|
-
".": {
|
|
14
|
-
"types": "./dist/index.d.ts",
|
|
15
|
-
"import": "./dist/index.js"
|
|
16
|
-
},
|
|
17
|
-
"./hooks": {
|
|
18
|
-
"types": "./dist/hooks/index.d.ts",
|
|
19
|
-
"import": "./dist/hooks/index.js"
|
|
20
|
-
},
|
|
21
|
-
"./components": {
|
|
22
|
-
"types": "./dist/components/index.d.ts",
|
|
23
|
-
"import": "./dist/components/index.js"
|
|
24
|
-
},
|
|
25
|
-
"./pages": {
|
|
26
|
-
"types": "./dist/pages/index.d.ts",
|
|
27
|
-
"import": "./dist/pages/index.js"
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
"files": [
|
|
31
|
-
"dist",
|
|
32
|
-
"src"
|
|
33
|
-
],
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "tsup",
|
|
36
|
-
"clean": "rm -rf dist",
|
|
37
|
-
"type-check": "tsc --noEmit",
|
|
38
|
-
"prepublishOnly": "pnpm clean && pnpm build"
|
|
39
|
-
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"@geenius-ai/shared": "workspace:*"
|
|
42
|
-
},
|
|
43
|
-
"peerDependencies": {
|
|
44
|
-
"convex": ">=1.0.0",
|
|
45
|
-
"react": ">=18.0.0"
|
|
46
|
-
},
|
|
47
|
-
"devDependencies": {
|
|
48
|
-
"@types/react": "^19.0.0",
|
|
49
|
-
"convex": "^1.34.0",
|
|
50
|
-
"react": "^19.2.4",
|
|
51
|
-
"tsup": "^8.5.1",
|
|
52
|
-
"typescript": "~6.0.2"
|
|
53
|
-
},
|
|
54
|
-
"engines": {
|
|
55
|
-
"node": ">=20.0.0"
|
|
56
|
-
},
|
|
57
|
-
"publishConfig": {
|
|
58
|
-
"access": "public"
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
// @geenius-ai/react — src/components/AILogTable.tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* AI log table — paginated, filterable.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { AILogEntry, AILogTableProps as BaseProps } from '@geenius-ai/shared'
|
|
8
|
-
|
|
9
|
-
export interface AILogTableComponentProps extends BaseProps {
|
|
10
|
-
logs: AILogEntry[]
|
|
11
|
-
isLoading: boolean
|
|
12
|
-
onRowClick?: (log: AILogEntry) => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function formatDuration(ms: number): string {
|
|
16
|
-
if (ms < 1000) return `${ms}ms`
|
|
17
|
-
return `${(ms / 1000).toFixed(1)}s`
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function formatCost(usd?: number): string {
|
|
21
|
-
if (!usd) return '—'
|
|
22
|
-
if (usd < 0.01) return `$${usd.toFixed(4)}`
|
|
23
|
-
return `$${usd.toFixed(2)}`
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function StatusBadge({ status }: { status: string }) {
|
|
27
|
-
return (
|
|
28
|
-
<span data-ai-status={status}>
|
|
29
|
-
{status === 'success' ? '✓' : '✗'} {status}
|
|
30
|
-
</span>
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function AILogTable(props: AILogTableComponentProps) {
|
|
35
|
-
if (props.isLoading) {
|
|
36
|
-
return <div data-ai-component="log-table" data-ai-loading>Loading logs…</div>
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (props.logs.length === 0) {
|
|
40
|
-
return <div data-ai-component="log-table" data-ai-empty>No AI logs yet</div>
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<div className={props.className} data-ai-component="log-table">
|
|
45
|
-
<table data-ai-table>
|
|
46
|
-
<thead>
|
|
47
|
-
<tr>
|
|
48
|
-
<th>Time</th>
|
|
49
|
-
<th>Model</th>
|
|
50
|
-
<th>Provider</th>
|
|
51
|
-
<th>Caller</th>
|
|
52
|
-
<th>Status</th>
|
|
53
|
-
<th>Duration</th>
|
|
54
|
-
<th>Tokens</th>
|
|
55
|
-
<th>Cost</th>
|
|
56
|
-
</tr>
|
|
57
|
-
</thead>
|
|
58
|
-
<tbody>
|
|
59
|
-
{props.logs.map((log) => (
|
|
60
|
-
<tr
|
|
61
|
-
key={log.requestId}
|
|
62
|
-
onClick={() => props.onRowClick?.(log)}
|
|
63
|
-
data-ai-log-row
|
|
64
|
-
data-ai-status={log.status}
|
|
65
|
-
>
|
|
66
|
-
<td data-ai-cell="time">
|
|
67
|
-
{new Date(log.timestamp).toLocaleTimeString()}
|
|
68
|
-
</td>
|
|
69
|
-
<td data-ai-cell="model">{log.model}</td>
|
|
70
|
-
<td data-ai-cell="provider">{log.provider}</td>
|
|
71
|
-
<td data-ai-cell="caller">{log.caller}</td>
|
|
72
|
-
<td data-ai-cell="status">
|
|
73
|
-
<StatusBadge status={log.status} />
|
|
74
|
-
</td>
|
|
75
|
-
<td data-ai-cell="duration">
|
|
76
|
-
{formatDuration(log.durationMs)}
|
|
77
|
-
</td>
|
|
78
|
-
<td data-ai-cell="tokens">
|
|
79
|
-
{log.totalTokens ?? '—'}
|
|
80
|
-
</td>
|
|
81
|
-
<td data-ai-cell="cost">
|
|
82
|
-
{formatCost(log.totalCostUsd)}
|
|
83
|
-
</td>
|
|
84
|
-
</tr>
|
|
85
|
-
))}
|
|
86
|
-
</tbody>
|
|
87
|
-
</table>
|
|
88
|
-
</div>
|
|
89
|
-
)
|
|
90
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// @geenius-ai/react — src/components/ChatWindow.tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Headless chat window: message list + input.
|
|
5
|
-
* Unstyled with data-* attributes for easy CSS targeting.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useState, useRef, useEffect, type FormEvent } from 'react'
|
|
9
|
-
import type { AIChatMessage, ChatWindowProps } from '@geenius-ai/shared'
|
|
10
|
-
import { useChat, type UseChatOptions } from '../hooks/useChat'
|
|
11
|
-
|
|
12
|
-
export interface ChatWindowComponentProps extends ChatWindowProps, UseChatOptions {
|
|
13
|
-
renderMessage?: (message: AIChatMessage) => React.ReactNode
|
|
14
|
-
renderInput?: (props: { value: string; onChange: (v: string) => void; onSubmit: () => void; isSending: boolean }) => React.ReactNode
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function ChatWindow(props: ChatWindowComponentProps) {
|
|
18
|
-
const [input, setInput] = useState('')
|
|
19
|
-
const messagesEndRef = useRef<HTMLDivElement>(null)
|
|
20
|
-
const chat = useChat(props)
|
|
21
|
-
|
|
22
|
-
// Auto-scroll to bottom
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
|
|
25
|
-
}, [chat.messages.length])
|
|
26
|
-
|
|
27
|
-
const handleSubmit = async (e?: FormEvent) => {
|
|
28
|
-
e?.preventDefault()
|
|
29
|
-
if (!input.trim() || chat.isSending) return
|
|
30
|
-
const content = input.trim()
|
|
31
|
-
setInput('')
|
|
32
|
-
await chat.sendMessage(content)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div className={props.className} data-ai-component="chat-window">
|
|
37
|
-
{/* Messages */}
|
|
38
|
-
<div data-ai-messages>
|
|
39
|
-
{chat.messages.length === 0 && (
|
|
40
|
-
<div data-ai-empty>
|
|
41
|
-
<p>Start a conversation</p>
|
|
42
|
-
</div>
|
|
43
|
-
)}
|
|
44
|
-
|
|
45
|
-
{chat.messages.map((msg: AIChatMessage) =>
|
|
46
|
-
props.renderMessage ? (
|
|
47
|
-
props.renderMessage(msg)
|
|
48
|
-
) : (
|
|
49
|
-
<div
|
|
50
|
-
key={msg.id}
|
|
51
|
-
data-ai-message
|
|
52
|
-
data-ai-role={msg.role}
|
|
53
|
-
>
|
|
54
|
-
<div data-ai-message-role>{msg.role}</div>
|
|
55
|
-
<div data-ai-message-content>{msg.content}</div>
|
|
56
|
-
{msg.tokens && (
|
|
57
|
-
<span data-ai-message-tokens>
|
|
58
|
-
{msg.tokens} tokens
|
|
59
|
-
</span>
|
|
60
|
-
)}
|
|
61
|
-
</div>
|
|
62
|
-
)
|
|
63
|
-
)}
|
|
64
|
-
|
|
65
|
-
{chat.isSending && (
|
|
66
|
-
<div data-ai-message data-ai-role="assistant" data-ai-loading>
|
|
67
|
-
<div data-ai-typing-indicator>
|
|
68
|
-
<span /><span /><span />
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
)}
|
|
72
|
-
|
|
73
|
-
<div ref={messagesEndRef} />
|
|
74
|
-
</div>
|
|
75
|
-
|
|
76
|
-
{/* Error */}
|
|
77
|
-
{chat.error && (
|
|
78
|
-
<div data-ai-error role="alert">
|
|
79
|
-
<span>{chat.error}</span>
|
|
80
|
-
<button onClick={chat.clearError} data-ai-dismiss>×</button>
|
|
81
|
-
</div>
|
|
82
|
-
)}
|
|
83
|
-
|
|
84
|
-
{/* Input */}
|
|
85
|
-
{props.renderInput ? (
|
|
86
|
-
props.renderInput({
|
|
87
|
-
value: input,
|
|
88
|
-
onChange: setInput,
|
|
89
|
-
onSubmit: handleSubmit,
|
|
90
|
-
isSending: chat.isSending,
|
|
91
|
-
})
|
|
92
|
-
) : (
|
|
93
|
-
<form onSubmit={handleSubmit} data-ai-input-form>
|
|
94
|
-
<textarea
|
|
95
|
-
value={input}
|
|
96
|
-
onChange={(e) => setInput(e.target.value)}
|
|
97
|
-
placeholder="Type a message…"
|
|
98
|
-
disabled={chat.isSending}
|
|
99
|
-
data-ai-input
|
|
100
|
-
onKeyDown={(e) => {
|
|
101
|
-
if (e.key === 'Enter' && !e.shiftKey) {
|
|
102
|
-
e.preventDefault()
|
|
103
|
-
handleSubmit()
|
|
104
|
-
}
|
|
105
|
-
}}
|
|
106
|
-
/>
|
|
107
|
-
<button
|
|
108
|
-
type="submit"
|
|
109
|
-
disabled={chat.isSending || !input.trim()}
|
|
110
|
-
data-ai-send
|
|
111
|
-
>
|
|
112
|
-
{chat.isSending ? 'Sending…' : 'Send'}
|
|
113
|
-
</button>
|
|
114
|
-
</form>
|
|
115
|
-
)}
|
|
116
|
-
</div>
|
|
117
|
-
)
|
|
118
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
// @geenius-ai/react — src/components/GenerationCard.tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Card displaying a generation result (text, image, audio, video).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { AIGenerationType } from '@geenius-ai/shared'
|
|
8
|
-
|
|
9
|
-
export interface GenerationCardProps {
|
|
10
|
-
type: AIGenerationType
|
|
11
|
-
content: string
|
|
12
|
-
model?: string
|
|
13
|
-
durationMs?: number
|
|
14
|
-
tokens?: number
|
|
15
|
-
cost?: number
|
|
16
|
-
error?: string
|
|
17
|
-
className?: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function GenerationCard(props: GenerationCardProps) {
|
|
21
|
-
return (
|
|
22
|
-
<div className={props.className} data-ai-component="generation-card" data-ai-type={props.type}>
|
|
23
|
-
{/* Header */}
|
|
24
|
-
<div data-ai-card-header>
|
|
25
|
-
<span data-ai-card-type>{props.type}</span>
|
|
26
|
-
{props.model && <span data-ai-card-model>{props.model}</span>}
|
|
27
|
-
{props.durationMs != null && (
|
|
28
|
-
<span data-ai-card-duration>{props.durationMs}ms</span>
|
|
29
|
-
)}
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
{/* Content */}
|
|
33
|
-
{props.error ? (
|
|
34
|
-
<div data-ai-card-error role="alert">{props.error}</div>
|
|
35
|
-
) : (
|
|
36
|
-
<div data-ai-card-content>
|
|
37
|
-
{props.type === 'image' ? (
|
|
38
|
-
<img
|
|
39
|
-
src={props.content.startsWith('http') ? props.content : `data:image/png;base64,${props.content}`}
|
|
40
|
-
alt="AI Generated"
|
|
41
|
-
data-ai-card-image
|
|
42
|
-
/>
|
|
43
|
-
) : props.type === 'audio' || props.type === 'speech' ? (
|
|
44
|
-
<audio controls data-ai-card-audio>
|
|
45
|
-
<source
|
|
46
|
-
src={props.content.startsWith('http') ? props.content : `data:audio/mp3;base64,${props.content}`}
|
|
47
|
-
type="audio/mp3"
|
|
48
|
-
/>
|
|
49
|
-
</audio>
|
|
50
|
-
) : props.type === 'video' ? (
|
|
51
|
-
<video controls data-ai-card-video>
|
|
52
|
-
<source src={props.content} type="video/mp4" />
|
|
53
|
-
</video>
|
|
54
|
-
) : (
|
|
55
|
-
<pre data-ai-card-text>{props.content}</pre>
|
|
56
|
-
)}
|
|
57
|
-
</div>
|
|
58
|
-
)}
|
|
59
|
-
|
|
60
|
-
{/* Footer */}
|
|
61
|
-
{(props.tokens || props.cost != null) && (
|
|
62
|
-
<div data-ai-card-footer>
|
|
63
|
-
{props.tokens && <span data-ai-card-tokens>{props.tokens} tokens</span>}
|
|
64
|
-
{props.cost != null && (
|
|
65
|
-
<span data-ai-card-cost>
|
|
66
|
-
${props.cost < 0.01 ? props.cost.toFixed(4) : props.cost.toFixed(2)}
|
|
67
|
-
</span>
|
|
68
|
-
)}
|
|
69
|
-
</div>
|
|
70
|
-
)}
|
|
71
|
-
</div>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
// @geenius-ai/react — src/components/ImageGenerator.tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Image generation UI — prompt, model selector, gallery.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useState, type FormEvent } from 'react'
|
|
8
|
-
import type { AIGenerationType } from '@geenius-ai/shared'
|
|
9
|
-
import { useImageGeneration, type UseImageGenerationOptions } from '../hooks/useImageGeneration'
|
|
10
|
-
|
|
11
|
-
export interface ImageGeneratorComponentProps extends UseImageGenerationOptions {
|
|
12
|
-
className?: string
|
|
13
|
-
availableModels?: string[]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function ImageGenerator(props: ImageGeneratorComponentProps) {
|
|
17
|
-
const [prompt, setPrompt] = useState('')
|
|
18
|
-
const [selectedModel, setSelectedModel] = useState(props.defaultModel ?? '')
|
|
19
|
-
const [size, setSize] = useState('1024x1024')
|
|
20
|
-
const [quality, setQuality] = useState<'standard' | 'hd'>('standard')
|
|
21
|
-
|
|
22
|
-
const { generate, images, isGenerating, error, clearImages, clearError } = useImageGeneration(props)
|
|
23
|
-
|
|
24
|
-
const handleSubmit = async (e: FormEvent) => {
|
|
25
|
-
e.preventDefault()
|
|
26
|
-
if (!prompt.trim() || isGenerating) return
|
|
27
|
-
await generate(prompt.trim(), { model: selectedModel || undefined, size, quality })
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<div className={props.className} data-ai-component="image-generator">
|
|
32
|
-
<form onSubmit={handleSubmit} data-ai-image-form>
|
|
33
|
-
<div data-ai-field="prompt">
|
|
34
|
-
<textarea
|
|
35
|
-
value={prompt}
|
|
36
|
-
onChange={(e) => setPrompt(e.target.value)}
|
|
37
|
-
placeholder="Describe the image you want to create…"
|
|
38
|
-
disabled={isGenerating}
|
|
39
|
-
data-ai-input
|
|
40
|
-
/>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<div data-ai-image-options>
|
|
44
|
-
{props.availableModels && (
|
|
45
|
-
<select
|
|
46
|
-
value={selectedModel}
|
|
47
|
-
onChange={(e) => setSelectedModel(e.target.value)}
|
|
48
|
-
disabled={isGenerating}
|
|
49
|
-
data-ai-select
|
|
50
|
-
>
|
|
51
|
-
<option value="">Default model</option>
|
|
52
|
-
{props.availableModels.map(m => (
|
|
53
|
-
<option key={m} value={m}>{m}</option>
|
|
54
|
-
))}
|
|
55
|
-
</select>
|
|
56
|
-
)}
|
|
57
|
-
|
|
58
|
-
<select value={size} onChange={(e) => setSize(e.target.value)} disabled={isGenerating} data-ai-select>
|
|
59
|
-
<option value="1024x1024">1024×1024</option>
|
|
60
|
-
<option value="1792x1024">1792×1024</option>
|
|
61
|
-
<option value="1024x1792">1024×1792</option>
|
|
62
|
-
<option value="512x512">512×512</option>
|
|
63
|
-
</select>
|
|
64
|
-
|
|
65
|
-
<select value={quality} onChange={(e) => setQuality(e.target.value as 'standard' | 'hd')} disabled={isGenerating} data-ai-select>
|
|
66
|
-
<option value="standard">Standard</option>
|
|
67
|
-
<option value="hd">HD</option>
|
|
68
|
-
</select>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<div data-ai-actions>
|
|
72
|
-
<button type="submit" disabled={isGenerating || !prompt.trim()} data-ai-submit>
|
|
73
|
-
{isGenerating ? 'Generating…' : 'Generate Image'}
|
|
74
|
-
</button>
|
|
75
|
-
{images.length > 0 && (
|
|
76
|
-
<button type="button" onClick={clearImages} data-ai-clear>Clear Gallery</button>
|
|
77
|
-
)}
|
|
78
|
-
</div>
|
|
79
|
-
</form>
|
|
80
|
-
|
|
81
|
-
{error && (
|
|
82
|
-
<div data-ai-error role="alert">
|
|
83
|
-
<span>{error}</span>
|
|
84
|
-
<button onClick={clearError}>×</button>
|
|
85
|
-
</div>
|
|
86
|
-
)}
|
|
87
|
-
|
|
88
|
-
{images.length > 0 && (
|
|
89
|
-
<div data-ai-image-gallery>
|
|
90
|
-
{images.map((img, i) => (
|
|
91
|
-
<div key={i} data-ai-image-card>
|
|
92
|
-
<img src={img.url} alt={img.prompt} data-ai-generated-image />
|
|
93
|
-
<div data-ai-image-meta>
|
|
94
|
-
<span data-ai-image-model>{img.model}</span>
|
|
95
|
-
<span data-ai-image-time>{new Date(img.timestamp).toLocaleTimeString()}</span>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
))}
|
|
99
|
-
</div>
|
|
100
|
-
)}
|
|
101
|
-
</div>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// @geenius-ai/react — src/components/ModelSelector.tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Model selector dropdown / list.
|
|
5
|
-
* Unstyled with data-* attributes.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { AIModel, AIProviderType } from '@geenius-ai/shared'
|
|
9
|
-
|
|
10
|
-
export interface ModelSelectorProps {
|
|
11
|
-
models: AIModel[]
|
|
12
|
-
selectedModel?: string
|
|
13
|
-
onSelect: (modelId: string) => void
|
|
14
|
-
className?: string
|
|
15
|
-
filterProvider?: AIProviderType
|
|
16
|
-
showCost?: boolean
|
|
17
|
-
disabled?: boolean
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function ModelSelector(props: ModelSelectorProps) {
|
|
21
|
-
const filtered = props.filterProvider
|
|
22
|
-
? props.models.filter(m => m.provider === props.filterProvider)
|
|
23
|
-
: props.models
|
|
24
|
-
|
|
25
|
-
const activeModels = filtered.filter(m => m.isActive)
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<select
|
|
29
|
-
className={props.className}
|
|
30
|
-
value={props.selectedModel ?? ''}
|
|
31
|
-
onChange={(e) => props.onSelect(e.target.value)}
|
|
32
|
-
disabled={props.disabled}
|
|
33
|
-
data-ai-component="model-selector"
|
|
34
|
-
>
|
|
35
|
-
<option value="" disabled>Select a model…</option>
|
|
36
|
-
{activeModels.map(model => (
|
|
37
|
-
<option key={model.id} value={model.id} data-ai-provider={model.provider}>
|
|
38
|
-
{model.displayName ?? model.name}
|
|
39
|
-
{props.showCost ? ` ($${model.inputCostPer1k}/$${model.outputCostPer1k}/1k)` : ''}
|
|
40
|
-
</option>
|
|
41
|
-
))}
|
|
42
|
-
</select>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// @geenius-ai/react — src/components/ModelTestRunner.tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Multi-tab model test runner.
|
|
5
|
-
* Extracted from ModelTestPage.tsx pattern found in 15+ apps.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useState, type FormEvent } from 'react'
|
|
9
|
-
import type { AIGenerationType } from '@geenius-ai/shared'
|
|
10
|
-
import { useModelTest, type UseModelTestOptions, type ModelTestResult } from '../hooks/useModelTest'
|
|
11
|
-
|
|
12
|
-
export interface ModelTestRunnerProps extends UseModelTestOptions {
|
|
13
|
-
className?: string
|
|
14
|
-
availableModels?: string[]
|
|
15
|
-
defaultTab?: AIGenerationType
|
|
16
|
-
renderResult?: (result: ModelTestResult) => React.ReactNode
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const TABS: { id: AIGenerationType; label: string }[] = [
|
|
20
|
-
{ id: 'text', label: 'Text Generation' },
|
|
21
|
-
{ id: 'image', label: 'Image Generation' },
|
|
22
|
-
{ id: 'audio', label: 'Text-to-Speech' },
|
|
23
|
-
{ id: 'transcription', label: 'Audio-to-Text' },
|
|
24
|
-
{ id: 'video', label: 'Video Generation' },
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
export function ModelTestRunner(props: ModelTestRunnerProps) {
|
|
28
|
-
const [activeTab, setActiveTab] = useState<AIGenerationType>(props.defaultTab ?? 'text')
|
|
29
|
-
const [prompt, setPrompt] = useState('')
|
|
30
|
-
const [selectedModel, setSelectedModel] = useState('')
|
|
31
|
-
const [batchMode, setBatchMode] = useState(false)
|
|
32
|
-
|
|
33
|
-
const test = useModelTest(props)
|
|
34
|
-
|
|
35
|
-
const handleSubmit = async (e: FormEvent) => {
|
|
36
|
-
e.preventDefault()
|
|
37
|
-
if (!prompt.trim()) return
|
|
38
|
-
|
|
39
|
-
if (batchMode && props.availableModels) {
|
|
40
|
-
await test.runBatchTest(props.availableModels, prompt.trim())
|
|
41
|
-
} else if (selectedModel) {
|
|
42
|
-
await test.runTest(selectedModel, prompt.trim(), activeTab)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<div className={props.className} data-ai-component="model-test-runner">
|
|
48
|
-
{/* Tabs */}
|
|
49
|
-
<div data-ai-tabs role="tablist">
|
|
50
|
-
{TABS.map(tab => (
|
|
51
|
-
<button
|
|
52
|
-
key={tab.id}
|
|
53
|
-
role="tab"
|
|
54
|
-
aria-selected={activeTab === tab.id}
|
|
55
|
-
onClick={() => setActiveTab(tab.id)}
|
|
56
|
-
data-ai-tab
|
|
57
|
-
data-ai-tab-active={activeTab === tab.id ? '' : undefined}
|
|
58
|
-
>
|
|
59
|
-
{tab.label}
|
|
60
|
-
</button>
|
|
61
|
-
))}
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
{/* Form */}
|
|
65
|
-
<form onSubmit={handleSubmit} data-ai-test-form>
|
|
66
|
-
<div data-ai-field="model">
|
|
67
|
-
<label htmlFor="test-model">Model</label>
|
|
68
|
-
<select
|
|
69
|
-
id="test-model"
|
|
70
|
-
value={selectedModel}
|
|
71
|
-
onChange={(e) => setSelectedModel(e.target.value)}
|
|
72
|
-
disabled={test.isRunning || batchMode}
|
|
73
|
-
data-ai-input
|
|
74
|
-
>
|
|
75
|
-
<option value="">Select model…</option>
|
|
76
|
-
{(props.availableModels ?? []).map(m => (
|
|
77
|
-
<option key={m} value={m}>{m}</option>
|
|
78
|
-
))}
|
|
79
|
-
</select>
|
|
80
|
-
</div>
|
|
81
|
-
|
|
82
|
-
<div data-ai-field="prompt">
|
|
83
|
-
<label htmlFor="test-prompt">Prompt</label>
|
|
84
|
-
<textarea
|
|
85
|
-
id="test-prompt"
|
|
86
|
-
value={prompt}
|
|
87
|
-
onChange={(e) => setPrompt(e.target.value)}
|
|
88
|
-
placeholder="Enter your test prompt…"
|
|
89
|
-
disabled={test.isRunning}
|
|
90
|
-
data-ai-input
|
|
91
|
-
/>
|
|
92
|
-
</div>
|
|
93
|
-
|
|
94
|
-
<div data-ai-actions>
|
|
95
|
-
<label data-ai-toggle>
|
|
96
|
-
<input
|
|
97
|
-
type="checkbox"
|
|
98
|
-
checked={batchMode}
|
|
99
|
-
onChange={(e) => setBatchMode(e.target.checked)}
|
|
100
|
-
disabled={test.isRunning}
|
|
101
|
-
/>
|
|
102
|
-
Test all models
|
|
103
|
-
</label>
|
|
104
|
-
<button type="submit" disabled={test.isRunning || !prompt.trim()} data-ai-submit>
|
|
105
|
-
{test.isRunning ? 'Running…' : (batchMode ? 'Run All' : 'Run Test')}
|
|
106
|
-
</button>
|
|
107
|
-
{test.results.length > 0 && (
|
|
108
|
-
<button type="button" onClick={test.clearResults} data-ai-clear>
|
|
109
|
-
Clear Results
|
|
110
|
-
</button>
|
|
111
|
-
)}
|
|
112
|
-
</div>
|
|
113
|
-
</form>
|
|
114
|
-
|
|
115
|
-
{/* Results */}
|
|
116
|
-
{test.results.length > 0 && (
|
|
117
|
-
<div data-ai-test-results>
|
|
118
|
-
{test.results.map((result, i) => (
|
|
119
|
-
props.renderResult ? props.renderResult(result) : (
|
|
120
|
-
<div key={i} data-ai-test-result data-ai-status={result.error ? 'error' : 'success'}>
|
|
121
|
-
<div data-ai-result-header>
|
|
122
|
-
<span data-ai-result-model>{result.model}</span>
|
|
123
|
-
<span data-ai-result-duration>{result.durationMs}ms</span>
|
|
124
|
-
{result.error && (
|
|
125
|
-
<span data-ai-result-error>{result.error}</span>
|
|
126
|
-
)}
|
|
127
|
-
</div>
|
|
128
|
-
{!result.error && (
|
|
129
|
-
<div data-ai-result-content>
|
|
130
|
-
{result.type === 'image' ? (
|
|
131
|
-
<img
|
|
132
|
-
src={result.result.startsWith('http') ? result.result : `data:image/png;base64,${result.result}`}
|
|
133
|
-
alt="Generated"
|
|
134
|
-
data-ai-result-image
|
|
135
|
-
/>
|
|
136
|
-
) : (
|
|
137
|
-
<pre data-ai-result-text>{result.result}</pre>
|
|
138
|
-
)}
|
|
139
|
-
</div>
|
|
140
|
-
)}
|
|
141
|
-
</div>
|
|
142
|
-
)
|
|
143
|
-
))}
|
|
144
|
-
</div>
|
|
145
|
-
)}
|
|
146
|
-
</div>
|
|
147
|
-
)
|
|
148
|
-
}
|