@geenius/ai 0.1.0

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 (165) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.env.example +2 -0
  3. package/.github/CODEOWNERS +1 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
  6. package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  7. package/.github/dependabot.yml +11 -0
  8. package/.github/workflows/ci.yml +23 -0
  9. package/.github/workflows/release.yml +29 -0
  10. package/.node-version +1 -0
  11. package/.nvmrc +1 -0
  12. package/.prettierrc +7 -0
  13. package/.project/ACCOUNT.yaml +4 -0
  14. package/.project/IDEAS.yaml +7 -0
  15. package/.project/PROJECT.yaml +11 -0
  16. package/.project/ROADMAP.yaml +15 -0
  17. package/CHANGELOG.md +15 -0
  18. package/CODE_OF_CONDUCT.md +26 -0
  19. package/CONTRIBUTING.md +61 -0
  20. package/LICENSE +21 -0
  21. package/README.md +1 -0
  22. package/SECURITY.md +18 -0
  23. package/SUPPORT.md +14 -0
  24. package/package.json +75 -0
  25. package/packages/convex/package.json +42 -0
  26. package/packages/convex/src/index.ts +8 -0
  27. package/packages/convex/src/mutations/messages.ts +29 -0
  28. package/packages/convex/src/queries/messages.ts +24 -0
  29. package/packages/convex/src/schema.ts +20 -0
  30. package/packages/convex/tsconfig.json +11 -0
  31. package/packages/convex/tsup.config.ts +17 -0
  32. package/packages/react/README.md +1 -0
  33. package/packages/react/package.json +60 -0
  34. package/packages/react/src/components/AILogTable.tsx +90 -0
  35. package/packages/react/src/components/ChatWindow.tsx +118 -0
  36. package/packages/react/src/components/GenerationCard.tsx +73 -0
  37. package/packages/react/src/components/ImageGenerator.tsx +103 -0
  38. package/packages/react/src/components/ModelSelector.tsx +44 -0
  39. package/packages/react/src/components/ModelTestRunner.tsx +148 -0
  40. package/packages/react/src/components/VoiceSelector.tsx +51 -0
  41. package/packages/react/src/components/index.ts +9 -0
  42. package/packages/react/src/hooks/index.ts +12 -0
  43. package/packages/react/src/hooks/useAI.ts +158 -0
  44. package/packages/react/src/hooks/useAILogs.ts +40 -0
  45. package/packages/react/src/hooks/useAIModels.ts +53 -0
  46. package/packages/react/src/hooks/useChat.ts +141 -0
  47. package/packages/react/src/hooks/useContentManager.ts +108 -0
  48. package/packages/react/src/hooks/useImageGeneration.ts +82 -0
  49. package/packages/react/src/hooks/useMemory.ts +161 -0
  50. package/packages/react/src/hooks/useModelTest.ts +126 -0
  51. package/packages/react/src/hooks/useRealtimeAudio.ts +203 -0
  52. package/packages/react/src/hooks/useSkills.ts +114 -0
  53. package/packages/react/src/hooks/useTextToSpeech.ts +99 -0
  54. package/packages/react/src/hooks/useTranscription.ts +119 -0
  55. package/packages/react/src/hooks/useVideoGeneration.ts +79 -0
  56. package/packages/react/src/index.ts +42 -0
  57. package/packages/react/src/pages/AILogsPage.tsx +98 -0
  58. package/packages/react/src/pages/ChatPage.tsx +42 -0
  59. package/packages/react/src/pages/ModelTestPage.tsx +33 -0
  60. package/packages/react/src/pages/index.ts +5 -0
  61. package/packages/react/tsconfig.json +26 -0
  62. package/packages/react/tsup.config.ts +22 -0
  63. package/packages/react-css/README.md +1 -0
  64. package/packages/react-css/package.json +45 -0
  65. package/packages/react-css/src/ai.css +857 -0
  66. package/packages/react-css/src/components/AILogTable.tsx +90 -0
  67. package/packages/react-css/src/components/ChatWindow.tsx +118 -0
  68. package/packages/react-css/src/components/GenerationCard.tsx +73 -0
  69. package/packages/react-css/src/components/ImageGenerator.tsx +103 -0
  70. package/packages/react-css/src/components/ModelSelector.tsx +44 -0
  71. package/packages/react-css/src/components/ModelTestRunner.tsx +148 -0
  72. package/packages/react-css/src/components/VoiceSelector.tsx +51 -0
  73. package/packages/react-css/src/components/index.ts +9 -0
  74. package/packages/react-css/src/hooks/index.ts +12 -0
  75. package/packages/react-css/src/hooks/useAI.ts +153 -0
  76. package/packages/react-css/src/hooks/useAILogs.ts +40 -0
  77. package/packages/react-css/src/hooks/useAIModels.ts +51 -0
  78. package/packages/react-css/src/hooks/useChat.ts +145 -0
  79. package/packages/react-css/src/hooks/useContentManager.ts +108 -0
  80. package/packages/react-css/src/hooks/useImageGeneration.ts +82 -0
  81. package/packages/react-css/src/hooks/useMemory.ts +161 -0
  82. package/packages/react-css/src/hooks/useModelTest.ts +122 -0
  83. package/packages/react-css/src/hooks/useRealtimeAudio.ts +203 -0
  84. package/packages/react-css/src/hooks/useSkills.ts +114 -0
  85. package/packages/react-css/src/hooks/useTextToSpeech.ts +99 -0
  86. package/packages/react-css/src/hooks/useTranscription.ts +119 -0
  87. package/packages/react-css/src/hooks/useVideoGeneration.ts +79 -0
  88. package/packages/react-css/src/index.ts +35 -0
  89. package/packages/react-css/src/pages/AILogsPage.tsx +98 -0
  90. package/packages/react-css/src/pages/ChatPage.tsx +42 -0
  91. package/packages/react-css/src/pages/ModelTestPage.tsx +33 -0
  92. package/packages/react-css/src/pages/index.ts +5 -0
  93. package/packages/react-css/src/styles.css +127 -0
  94. package/packages/react-css/tsconfig.json +26 -0
  95. package/packages/react-css/tsup.config.ts +2 -0
  96. package/packages/shared/README.md +1 -0
  97. package/packages/shared/package.json +71 -0
  98. package/packages/shared/src/__tests__/ai.test.ts +67 -0
  99. package/packages/shared/src/ai-client.ts +243 -0
  100. package/packages/shared/src/config.ts +235 -0
  101. package/packages/shared/src/content.ts +249 -0
  102. package/packages/shared/src/convex/helpers.ts +163 -0
  103. package/packages/shared/src/convex/index.ts +16 -0
  104. package/packages/shared/src/convex/schemas.ts +146 -0
  105. package/packages/shared/src/convex/validators.ts +136 -0
  106. package/packages/shared/src/index.ts +107 -0
  107. package/packages/shared/src/memory.ts +197 -0
  108. package/packages/shared/src/providers/base.ts +103 -0
  109. package/packages/shared/src/providers/elevenlabs.ts +155 -0
  110. package/packages/shared/src/providers/index.ts +28 -0
  111. package/packages/shared/src/providers/openai-compatible.ts +286 -0
  112. package/packages/shared/src/providers/registry.ts +113 -0
  113. package/packages/shared/src/providers/replicate-fal.ts +230 -0
  114. package/packages/shared/src/skills.ts +273 -0
  115. package/packages/shared/src/types.ts +501 -0
  116. package/packages/shared/tsconfig.json +25 -0
  117. package/packages/shared/tsup.config.ts +22 -0
  118. package/packages/shared/vitest.config.ts +4 -0
  119. package/packages/solidjs/README.md +1 -0
  120. package/packages/solidjs/package.json +59 -0
  121. package/packages/solidjs/src/components/ChatWindow.tsx +78 -0
  122. package/packages/solidjs/src/components/GenerationCard.tsx +62 -0
  123. package/packages/solidjs/src/components/ModelTestRunner.tsx +119 -0
  124. package/packages/solidjs/src/components/index.ts +5 -0
  125. package/packages/solidjs/src/index.ts +32 -0
  126. package/packages/solidjs/src/pages/ChatPage.tsx +22 -0
  127. package/packages/solidjs/src/pages/ModelTestPage.tsx +22 -0
  128. package/packages/solidjs/src/pages/index.ts +4 -0
  129. package/packages/solidjs/src/primitives/createAI.ts +79 -0
  130. package/packages/solidjs/src/primitives/createChat.ts +100 -0
  131. package/packages/solidjs/src/primitives/createContentManager.ts +61 -0
  132. package/packages/solidjs/src/primitives/createImageGeneration.ts +46 -0
  133. package/packages/solidjs/src/primitives/createMemory.ts +127 -0
  134. package/packages/solidjs/src/primitives/createModelTest.ts +89 -0
  135. package/packages/solidjs/src/primitives/createSkills.ts +83 -0
  136. package/packages/solidjs/src/primitives/createTextToSpeech.ts +56 -0
  137. package/packages/solidjs/src/primitives/createVideoGeneration.ts +46 -0
  138. package/packages/solidjs/src/primitives/index.ts +8 -0
  139. package/packages/solidjs/tsconfig.json +27 -0
  140. package/packages/solidjs/tsup.config.ts +21 -0
  141. package/packages/solidjs-css/README.md +1 -0
  142. package/packages/solidjs-css/package.json +44 -0
  143. package/packages/solidjs-css/src/ai.css +857 -0
  144. package/packages/solidjs-css/src/components/ChatWindow.tsx +78 -0
  145. package/packages/solidjs-css/src/components/GenerationCard.tsx +62 -0
  146. package/packages/solidjs-css/src/components/ModelTestRunner.tsx +119 -0
  147. package/packages/solidjs-css/src/components/index.ts +5 -0
  148. package/packages/solidjs-css/src/index.ts +26 -0
  149. package/packages/solidjs-css/src/pages/ChatPage.tsx +22 -0
  150. package/packages/solidjs-css/src/pages/ModelTestPage.tsx +22 -0
  151. package/packages/solidjs-css/src/pages/index.ts +4 -0
  152. package/packages/solidjs-css/src/primitives/createAI.ts +79 -0
  153. package/packages/solidjs-css/src/primitives/createChat.ts +100 -0
  154. package/packages/solidjs-css/src/primitives/createContentManager.ts +61 -0
  155. package/packages/solidjs-css/src/primitives/createImageGeneration.ts +46 -0
  156. package/packages/solidjs-css/src/primitives/createMemory.ts +127 -0
  157. package/packages/solidjs-css/src/primitives/createModelTest.ts +89 -0
  158. package/packages/solidjs-css/src/primitives/createSkills.ts +83 -0
  159. package/packages/solidjs-css/src/primitives/createTextToSpeech.ts +56 -0
  160. package/packages/solidjs-css/src/primitives/createVideoGeneration.ts +46 -0
  161. package/packages/solidjs-css/src/primitives/index.ts +1 -0
  162. package/packages/solidjs-css/src/styles.css +127 -0
  163. package/packages/solidjs-css/tsconfig.json +27 -0
  164. package/packages/solidjs-css/tsup.config.ts +2 -0
  165. package/pnpm-workspace.yaml +2 -0
@@ -0,0 +1,119 @@
1
+ // @geenius-ai/react — src/hooks/useTranscription.ts
2
+
3
+ /**
4
+ * Audio transcription hook (Whisper, Groq ASR, etc.)
5
+ */
6
+
7
+ import { useState, useCallback, useRef } from 'react'
8
+ import { useAction } from 'convex/react'
9
+
10
+ export interface TranscriptionResult {
11
+ text: string
12
+ timestamp: number
13
+ durationMs: number
14
+ }
15
+
16
+ export interface UseTranscriptionOptions {
17
+ transcribeAction: any
18
+ defaultModel?: string
19
+ /** Enable browser microphone recording */
20
+ enableMicrophone?: boolean
21
+ }
22
+
23
+ export interface UseTranscriptionReturn {
24
+ transcribe: (audioBase64: string, options?: {
25
+ model?: string
26
+ language?: string
27
+ prompt?: string
28
+ }) => Promise<string>
29
+ startRecording: () => Promise<void>
30
+ stopRecording: () => Promise<string>
31
+ isTranscribing: boolean
32
+ isRecording: boolean
33
+ lastResult: TranscriptionResult | null
34
+ error: string | null
35
+ clearError: () => void
36
+ }
37
+
38
+ export function useTranscription(options: UseTranscriptionOptions): UseTranscriptionReturn {
39
+ const [isTranscribing, setIsTranscribing] = useState(false)
40
+ const [isRecording, setIsRecording] = useState(false)
41
+ const [lastResult, setLastResult] = useState<TranscriptionResult | null>(null)
42
+ const [error, setError] = useState<string | null>(null)
43
+ const mediaRecorderRef = useRef<MediaRecorder | null>(null)
44
+ const chunksRef = useRef<Blob[]>([])
45
+ const action = useAction(options.transcribeAction)
46
+
47
+ const transcribe = useCallback(async (audioBase64: string, opts?: {
48
+ model?: string; language?: string; prompt?: string
49
+ }) => {
50
+ setIsTranscribing(true)
51
+ setError(null)
52
+ const start = Date.now()
53
+ try {
54
+ const text = await action({
55
+ audio: audioBase64,
56
+ model: opts?.model ?? options.defaultModel,
57
+ language: opts?.language,
58
+ prompt: opts?.prompt,
59
+ })
60
+ setLastResult({ text, timestamp: Date.now(), durationMs: Date.now() - start })
61
+ return text
62
+ } catch (err) {
63
+ const msg = err instanceof Error ? err.message : 'Transcription failed'
64
+ setError(msg)
65
+ throw err
66
+ } finally {
67
+ setIsTranscribing(false)
68
+ }
69
+ }, [action, options.defaultModel])
70
+
71
+ const startRecording = useCallback(async () => {
72
+ if (!options.enableMicrophone) throw new Error('Microphone not enabled')
73
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
74
+ const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' })
75
+ chunksRef.current = []
76
+ mediaRecorder.ondataavailable = (e) => {
77
+ if (e.data.size > 0) chunksRef.current.push(e.data)
78
+ }
79
+ mediaRecorder.start()
80
+ mediaRecorderRef.current = mediaRecorder
81
+ setIsRecording(true)
82
+ }, [options.enableMicrophone])
83
+
84
+ const stopRecording = useCallback(async () => {
85
+ return new Promise<string>((resolve, reject) => {
86
+ const recorder = mediaRecorderRef.current
87
+ if (!recorder) { reject(new Error('Not recording')); return }
88
+
89
+ recorder.onstop = async () => {
90
+ setIsRecording(false)
91
+ const blob = new Blob(chunksRef.current, { type: 'audio/webm' })
92
+ const arrayBuffer = await blob.arrayBuffer()
93
+ const bytes = new Uint8Array(arrayBuffer)
94
+ let binary = ''
95
+ for (let i = 0; i < bytes.length; i++) {
96
+ binary += String.fromCharCode(bytes[i]!)
97
+ }
98
+ const base64 = btoa(binary)
99
+
100
+ // Stop all tracks
101
+ recorder.stream.getTracks().forEach(t => t.stop())
102
+
103
+ try {
104
+ const text = await transcribe(base64)
105
+ resolve(text)
106
+ } catch (err) {
107
+ reject(err)
108
+ }
109
+ }
110
+ recorder.stop()
111
+ })
112
+ }, [transcribe])
113
+
114
+ return {
115
+ transcribe, startRecording, stopRecording,
116
+ isTranscribing, isRecording, lastResult, error,
117
+ clearError: () => setError(null),
118
+ }
119
+ }
@@ -0,0 +1,79 @@
1
+ // @geenius-ai/react — src/hooks/useVideoGeneration.ts
2
+
3
+ /**
4
+ * Video generation hook (Replicate, Fal, etc.)
5
+ */
6
+
7
+ import { useState, useCallback } from 'react'
8
+ import { useAction } from 'convex/react'
9
+
10
+ export interface GeneratedVideo {
11
+ url: string
12
+ prompt: string
13
+ model: string
14
+ timestamp: number
15
+ }
16
+
17
+ export interface UseVideoGenerationOptions {
18
+ generateVideoAction: any
19
+ defaultModel?: string
20
+ }
21
+
22
+ export interface UseVideoGenerationReturn {
23
+ generate: (prompt: string, options?: {
24
+ model?: string
25
+ duration?: number
26
+ aspectRatio?: '16:9' | '9:16' | '1:1'
27
+ resolution?: '720p' | '1080p' | '4k'
28
+ startImage?: string
29
+ endImage?: string
30
+ }) => Promise<string>
31
+ videos: GeneratedVideo[]
32
+ isGenerating: boolean
33
+ error: string | null
34
+ clearVideos: () => void
35
+ clearError: () => void
36
+ }
37
+
38
+ export function useVideoGeneration(options: UseVideoGenerationOptions): UseVideoGenerationReturn {
39
+ const [videos, setVideos] = useState<GeneratedVideo[]>([])
40
+ const [isGenerating, setIsGenerating] = useState(false)
41
+ const [error, setError] = useState<string | null>(null)
42
+ const action = useAction(options.generateVideoAction)
43
+
44
+ const generate = useCallback(async (prompt: string, opts?: {
45
+ model?: string; duration?: number
46
+ aspectRatio?: '16:9' | '9:16' | '1:1'
47
+ resolution?: '720p' | '1080p' | '4k'
48
+ startImage?: string; endImage?: string
49
+ }) => {
50
+ setIsGenerating(true)
51
+ setError(null)
52
+ try {
53
+ const model = opts?.model ?? options.defaultModel ?? 'minimax/video-01'
54
+ const result = await action({
55
+ prompt, model,
56
+ duration: opts?.duration,
57
+ aspectRatio: opts?.aspectRatio,
58
+ resolution: opts?.resolution,
59
+ startImage: opts?.startImage,
60
+ endImage: opts?.endImage,
61
+ })
62
+ const vid: GeneratedVideo = { url: result, prompt, model, timestamp: Date.now() }
63
+ setVideos(prev => [vid, ...prev])
64
+ return result
65
+ } catch (err) {
66
+ const msg = err instanceof Error ? err.message : 'Video generation failed'
67
+ setError(msg)
68
+ throw err
69
+ } finally {
70
+ setIsGenerating(false)
71
+ }
72
+ }, [action, options.defaultModel])
73
+
74
+ return {
75
+ generate, videos, isGenerating, error,
76
+ clearVideos: () => setVideos([]),
77
+ clearError: () => setError(null),
78
+ }
79
+ }
@@ -0,0 +1,35 @@
1
+ // @geenius-ai/react-css — Independent barrel (vanilla CSS variant)
2
+ // ALL imports from LOCAL files + @geenius-ai/shared — NOT from @geenius-ai/react
3
+
4
+ // Hooks — Core
5
+ export { useAI, type UseAIOptions, type UseAIReturn } from './hooks/useAI'
6
+ export { useChat, type UseChatOptions, type UseChatReturn } from './hooks/useChat'
7
+ export { useAILogs, type UseAILogsOptions, type UseAILogsReturn } from './hooks/useAILogs'
8
+ export { useModelTest, type UseModelTestOptions, type UseModelTestReturn, type ModelTestResult } from './hooks/useModelTest'
9
+ export { useAIModels, type UseAIModelsOptions, type UseAIModelsReturn } from './hooks/useAIModels'
10
+
11
+ // Hooks — Multi-modal
12
+ export { useImageGeneration, type UseImageGenerationOptions, type UseImageGenerationReturn, type GeneratedImage } from './hooks/useImageGeneration'
13
+ export { useTextToSpeech, type UseTextToSpeechOptions, type UseTextToSpeechReturn } from './hooks/useTextToSpeech'
14
+ export { useVideoGeneration, type UseVideoGenerationOptions, type UseVideoGenerationReturn, type GeneratedVideo } from './hooks/useVideoGeneration'
15
+ export { useTranscription, type UseTranscriptionOptions, type UseTranscriptionReturn, type TranscriptionResult } from './hooks/useTranscription'
16
+ export { useRealtimeAudio, type UseRealtimeAudioOptions, type UseRealtimeAudioReturn } from './hooks/useRealtimeAudio'
17
+
18
+ // Hooks — Content, Memory, Skills
19
+ export { useContentManager, type UseContentManagerOptions, type UseContentManagerReturn } from './hooks/useContentManager'
20
+ export { useMemory, type UseMemoryOptions, type UseMemoryReturn } from './hooks/useMemory'
21
+ export { useSkills, type UseSkillsOptions, type UseSkillsReturn } from './hooks/useSkills'
22
+
23
+ // Components
24
+ export { ChatWindow, type ChatWindowComponentProps } from './components/ChatWindow'
25
+ export { ModelSelector, type ModelSelectorProps } from './components/ModelSelector'
26
+ export { AILogTable, type AILogTableComponentProps } from './components/AILogTable'
27
+ export { ModelTestRunner, type ModelTestRunnerProps } from './components/ModelTestRunner'
28
+ export { GenerationCard, type GenerationCardProps } from './components/GenerationCard'
29
+ export { ImageGenerator, type ImageGeneratorComponentProps } from './components/ImageGenerator'
30
+ export { VoiceSelector, type VoiceSelectorComponentProps } from './components/VoiceSelector'
31
+
32
+ // Pages
33
+ export { ChatPage, type ChatPageProps } from './pages/ChatPage'
34
+ export { ModelTestPage, type ModelTestPageProps } from './pages/ModelTestPage'
35
+ export { AILogsPage, type AILogsPageProps } from './pages/AILogsPage'
@@ -0,0 +1,98 @@
1
+ // @geenius-ai/react — src/pages/AILogsPage.tsx
2
+
3
+ /**
4
+ * AI logs dashboard page with stats and log table.
5
+ */
6
+
7
+ import { useState } from 'react'
8
+ import { AILogTable } from '../components/AILogTable'
9
+ import { useAILogs, type UseAILogsOptions } from '../hooks/useAILogs'
10
+ import type { AIRequestStatus, AILogEntry } from '@geenius-ai/shared'
11
+
12
+ export interface AILogsPageProps {
13
+ /** Query reference: api.ai.listLogs */
14
+ listLogsQuery: any
15
+ className?: string
16
+ title?: string
17
+ onRowClick?: (log: AILogEntry) => void
18
+ }
19
+
20
+ export function AILogsPage(props: AILogsPageProps) {
21
+ const [modelFilter, setModelFilter] = useState('')
22
+ const [statusFilter, setStatusFilter] = useState<AIRequestStatus | ''>('')
23
+
24
+ const { logs, isLoading } = useAILogs({
25
+ listLogsQuery: props.listLogsQuery,
26
+ filters: {
27
+ model: modelFilter || undefined,
28
+ status: (statusFilter || undefined) as AIRequestStatus | undefined,
29
+ },
30
+ limit: 100,
31
+ })
32
+
33
+ // Calculate quick stats
34
+ const totalCalls = logs.length
35
+ const successCalls = logs.filter(l => l.status === 'success').length
36
+ const errorCalls = totalCalls - successCalls
37
+ const totalCost = logs.reduce((sum, l) => sum + (l.totalCostUsd ?? 0), 0)
38
+ const totalTokens = logs.reduce((sum, l) => sum + (l.totalTokens ?? 0), 0)
39
+
40
+ return (
41
+ <div className={props.className} data-ai-page="logs">
42
+ <div data-ai-page-header>
43
+ <h1 data-ai-page-title>{props.title ?? 'AI Logs'}</h1>
44
+ </div>
45
+
46
+ {/* Stats cards */}
47
+ <div data-ai-stats-grid>
48
+ <div data-ai-stat>
49
+ <span data-ai-stat-label>Total Calls</span>
50
+ <span data-ai-stat-value>{totalCalls}</span>
51
+ </div>
52
+ <div data-ai-stat data-ai-status="success">
53
+ <span data-ai-stat-label>Success</span>
54
+ <span data-ai-stat-value>{successCalls}</span>
55
+ </div>
56
+ <div data-ai-stat data-ai-status="error">
57
+ <span data-ai-stat-label>Errors</span>
58
+ <span data-ai-stat-value>{errorCalls}</span>
59
+ </div>
60
+ <div data-ai-stat>
61
+ <span data-ai-stat-label>Total Cost</span>
62
+ <span data-ai-stat-value>${totalCost.toFixed(2)}</span>
63
+ </div>
64
+ <div data-ai-stat>
65
+ <span data-ai-stat-label>Total Tokens</span>
66
+ <span data-ai-stat-value>{totalTokens.toLocaleString()}</span>
67
+ </div>
68
+ </div>
69
+
70
+ {/* Filters */}
71
+ <div data-ai-filters>
72
+ <input
73
+ type="text"
74
+ placeholder="Filter by model…"
75
+ value={modelFilter}
76
+ onChange={(e) => setModelFilter(e.target.value)}
77
+ data-ai-filter-input
78
+ />
79
+ <select
80
+ value={statusFilter}
81
+ onChange={(e) => setStatusFilter(e.target.value as AIRequestStatus | '')}
82
+ data-ai-filter-select
83
+ >
84
+ <option value="">All statuses</option>
85
+ <option value="success">Success</option>
86
+ <option value="error">Error</option>
87
+ </select>
88
+ </div>
89
+
90
+ {/* Log table */}
91
+ <AILogTable
92
+ logs={logs}
93
+ isLoading={isLoading}
94
+ onRowClick={props.onRowClick}
95
+ />
96
+ </div>
97
+ )
98
+ }
@@ -0,0 +1,42 @@
1
+ // @geenius-ai/react — src/pages/ChatPage.tsx
2
+
3
+ /**
4
+ * Full chatbot page — sidebar + conversation.
5
+ * Compose with your own layout and styling.
6
+ */
7
+
8
+ import { useState } from 'react'
9
+ import { ChatWindow, type ChatWindowComponentProps } from '../components/ChatWindow'
10
+
11
+ export interface ChatPageProps extends Omit<ChatWindowComponentProps, 'conversationId'> {
12
+ className?: string
13
+ /** Initial conversation ID */
14
+ conversationId?: string
15
+ }
16
+
17
+ export function ChatPage(props: ChatPageProps) {
18
+ const [activeConversationId, setActiveConversationId] = useState<string | undefined>(props.conversationId)
19
+
20
+ return (
21
+ <div className={props.className} data-ai-page="chat">
22
+ <div data-ai-page-header>
23
+ <h1 data-ai-page-title>AI Chat</h1>
24
+ <button
25
+ onClick={() => setActiveConversationId(undefined)}
26
+ data-ai-new-chat
27
+ >
28
+ New Chat
29
+ </button>
30
+ </div>
31
+
32
+ <ChatWindow
33
+ {...props}
34
+ conversationId={activeConversationId}
35
+ onNewConversation={(id) => {
36
+ setActiveConversationId(id)
37
+ props.onNewConversation?.(id)
38
+ }}
39
+ />
40
+ </div>
41
+ )
42
+ }
@@ -0,0 +1,33 @@
1
+ // @geenius-ai/react — src/pages/ModelTestPage.tsx
2
+
3
+ /**
4
+ * Model test page — extracted from the pattern used in 15+ apps.
5
+ */
6
+
7
+ import { ModelTestRunner, type ModelTestRunnerProps } from '../components/ModelTestRunner'
8
+ import { DEFAULT_MODELS } from '@geenius-ai/shared'
9
+ import type { AIGenerationType } from '@geenius-ai/shared'
10
+
11
+ export interface ModelTestPageProps extends ModelTestRunnerProps {
12
+ title?: string
13
+ }
14
+
15
+ export function ModelTestPage(props: ModelTestPageProps) {
16
+ const models = props.availableModels ?? DEFAULT_MODELS.map(m => m.id)
17
+
18
+ return (
19
+ <div className={props.className} data-ai-page="model-test">
20
+ <div data-ai-page-header>
21
+ <h1 data-ai-page-title>{props.title ?? 'Model Test Lab'}</h1>
22
+ <p data-ai-page-subtitle>
23
+ Test AI models individually or compare them side-by-side
24
+ </p>
25
+ </div>
26
+
27
+ <ModelTestRunner
28
+ {...props}
29
+ availableModels={models}
30
+ />
31
+ </div>
32
+ )
33
+ }
@@ -0,0 +1,5 @@
1
+ // @geenius-ai/react — src/pages/index.ts
2
+
3
+ export { ChatPage, type ChatPageProps } from './ChatPage'
4
+ export { ModelTestPage, type ModelTestPageProps } from './ModelTestPage'
5
+ export { AILogsPage, type AILogsPageProps } from './AILogsPage'
@@ -0,0 +1,127 @@
1
+ /* @geenius-ai — styles.css (premium vanilla CSS for AI components)
2
+ * oklch colors, dark-mode-first, responsive, data-* targeted
3
+ */
4
+
5
+ :root {
6
+ --gai-bg: oklch(0.13 0.02 260);
7
+ --gai-surface: oklch(0.18 0.02 260);
8
+ --gai-surface-hover: oklch(0.22 0.02 260);
9
+ --gai-text: oklch(0.94 0.01 260);
10
+ --gai-text-muted: oklch(0.62 0.02 260);
11
+ --gai-border: oklch(0.28 0.02 260);
12
+ --gai-primary: oklch(0.65 0.22 265);
13
+ --gai-primary-dim: oklch(0.65 0.22 265 / 0.12);
14
+ --gai-success: oklch(0.68 0.18 155);
15
+ --gai-error: oklch(0.62 0.2 25);
16
+ --gai-warning: oklch(0.7 0.18 80);
17
+ --gai-radius: 12px;
18
+ --gai-radius-sm: 8px;
19
+ --gai-font: system-ui, -apple-system, sans-serif;
20
+ --gai-font-mono: 'JetBrains Mono', 'Fira Code', monospace;
21
+ --gai-transition: 180ms cubic-bezier(0.4, 0, 0.2, 1);
22
+ }
23
+
24
+ [data-theme="light"] {
25
+ --gai-bg: oklch(0.97 0.005 260);
26
+ --gai-surface: oklch(1 0 0);
27
+ --gai-surface-hover: oklch(0.96 0.005 260);
28
+ --gai-text: oklch(0.15 0.02 260);
29
+ --gai-text-muted: oklch(0.5 0.02 260);
30
+ --gai-border: oklch(0.88 0.01 260);
31
+ }
32
+
33
+ /* ─── Chat Window ───────────────────────────────── */
34
+ [data-chat-window] {
35
+ display: flex; flex-direction: column; height: 100%;
36
+ font-family: var(--gai-font); color: var(--gai-text); background: var(--gai-bg);
37
+ border: 1px solid var(--gai-border); border-radius: var(--gai-radius);
38
+ overflow: hidden;
39
+ }
40
+ [data-chat-header] { display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: var(--gai-surface); border-bottom: 1px solid var(--gai-border); }
41
+ [data-chat-messages] { flex: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 0.75rem; }
42
+ [data-chat-message] { max-width: 80%; padding: 0.75rem 1rem; border-radius: var(--gai-radius); font-size: 0.875rem; line-height: 1.5; }
43
+ [data-chat-message][data-role="user"] { align-self: flex-end; background: var(--gai-primary); color: white; border-bottom-right-radius: 4px; }
44
+ [data-chat-message][data-role="assistant"] { align-self: flex-start; background: var(--gai-surface); border: 1px solid var(--gai-border); border-bottom-left-radius: 4px; }
45
+ [data-chat-input-form] { display: flex; gap: 0.5rem; padding: 0.75rem 1rem; border-top: 1px solid var(--gai-border); background: var(--gai-surface); }
46
+ [data-chat-input] { flex: 1; padding: 0.625rem 1rem; border: 1px solid var(--gai-border); border-radius: var(--gai-radius-sm); background: var(--gai-bg); color: var(--gai-text); font-size: 0.875rem; outline: none; transition: border-color var(--gai-transition); }
47
+ [data-chat-input]:focus { border-color: var(--gai-primary); }
48
+ [data-chat-send] { padding: 0.625rem 1.25rem; background: var(--gai-primary); color: white; border: none; border-radius: var(--gai-radius-sm); font-weight: 600; font-size: 0.8125rem; cursor: pointer; transition: opacity var(--gai-transition); }
49
+ [data-chat-send]:hover { opacity: 0.85; }
50
+ [data-chat-send]:disabled { opacity: 0.4; cursor: not-allowed; }
51
+ [data-chat-typing] { font-size: 0.75rem; color: var(--gai-text-muted); font-style: italic; padding: 0.25rem 1rem; }
52
+
53
+ /* ─── Model Selector ────────────────────────────── */
54
+ [data-model-selector] { position: relative; }
55
+ [data-model-selector-trigger] { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; border: 1px solid var(--gai-border); border-radius: var(--gai-radius-sm); background: var(--gai-surface); color: var(--gai-text); font-size: 0.8125rem; cursor: pointer; transition: border-color var(--gai-transition); }
56
+ [data-model-selector-trigger]:hover { border-color: var(--gai-primary); }
57
+ [data-model-selector-dropdown] { position: absolute; top: 100%; left: 0; right: 0; margin-top: 4px; background: var(--gai-surface); border: 1px solid var(--gai-border); border-radius: var(--gai-radius-sm); box-shadow: 0 8px 24px oklch(0 0 0 / 0.2); z-index: 50; max-height: 240px; overflow-y: auto; }
58
+ [data-model-option] { display: flex; align-items: center; gap: 0.5rem; padding: 0.625rem 1rem; font-size: 0.8125rem; cursor: pointer; transition: background var(--gai-transition); }
59
+ [data-model-option]:hover { background: var(--gai-surface-hover); }
60
+ [data-model-option][data-selected] { color: var(--gai-primary); font-weight: 600; }
61
+
62
+ /* ─── AI Log Table ──────────────────────────────── */
63
+ [data-ai-log-table] { border: 1px solid var(--gai-border); border-radius: var(--gai-radius); overflow: hidden; }
64
+ [data-ai-log-header] { display: grid; grid-template-columns: 1fr 1fr 0.6fr 0.6fr 0.4fr; gap: 0; padding: 0.5rem 1rem; background: var(--gai-surface); font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--gai-text-muted); }
65
+ [data-ai-log-row] { display: grid; grid-template-columns: 1fr 1fr 0.6fr 0.6fr 0.4fr; gap: 0; padding: 0.625rem 1rem; border-top: 1px solid oklch(0 0 0 / 0.04); font-size: 0.8125rem; transition: background var(--gai-transition); }
66
+ [data-ai-log-row]:hover { background: var(--gai-surface-hover); }
67
+ [data-ai-log-empty] { text-align: center; padding: 3rem 1rem; color: var(--gai-text-muted); }
68
+ [data-ai-log-skeleton] { height: 2.5rem; background: var(--gai-surface); border-radius: var(--gai-radius-sm); animation: gai-pulse 1.5s infinite; margin-bottom: 0.25rem; }
69
+
70
+ /* ─── Model Test Runner ─────────────────────────── */
71
+ [data-model-test] { border: 1px solid var(--gai-border); border-radius: var(--gai-radius); overflow: hidden; }
72
+ [data-model-test-header] { padding: 1rem; background: var(--gai-surface); border-bottom: 1px solid var(--gai-border); }
73
+ [data-model-test-form] { display: flex; flex-direction: column; gap: 0.75rem; padding: 1rem; }
74
+ [data-model-test-input] { padding: 0.625rem 1rem; border: 1px solid var(--gai-border); border-radius: var(--gai-radius-sm); background: var(--gai-bg); color: var(--gai-text); font-size: 0.875rem; }
75
+ [data-model-test-run] { align-self: flex-start; padding: 0.625rem 1.5rem; background: var(--gai-primary); color: white; border: none; border-radius: var(--gai-radius-sm); font-weight: 600; cursor: pointer; }
76
+ [data-model-test-results] { padding: 1rem; display: flex; flex-direction: column; gap: 0.5rem; }
77
+ [data-model-test-result] { padding: 0.75rem; border: 1px solid var(--gai-border); border-radius: var(--gai-radius-sm); font-size: 0.8125rem; }
78
+ [data-model-test-result][data-success="true"] { border-left: 3px solid var(--gai-success); }
79
+ [data-model-test-result][data-success="false"] { border-left: 3px solid var(--gai-error); }
80
+
81
+ /* ─── Generation Card ───────────────────────────── */
82
+ [data-generation-card] { border: 1px solid var(--gai-border); border-radius: var(--gai-radius); overflow: hidden; transition: box-shadow var(--gai-transition); }
83
+ [data-generation-card]:hover { box-shadow: 0 4px 16px oklch(0 0 0 / 0.12); }
84
+ [data-generation-preview] { aspect-ratio: 16/9; background: var(--gai-surface); display: flex; align-items: center; justify-content: center; overflow: hidden; }
85
+ [data-generation-preview] img { width: 100%; height: 100%; object-fit: cover; }
86
+ [data-generation-meta] { padding: 0.75rem 1rem; }
87
+ [data-generation-type] { font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--gai-primary); }
88
+ [data-generation-prompt] { font-size: 0.8125rem; margin-top: 0.25rem; color: var(--gai-text); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
89
+ [data-generation-actions] { display: flex; gap: 0.5rem; padding: 0.5rem 1rem; border-top: 1px solid var(--gai-border); }
90
+
91
+ /* ─── Image Generator ───────────────────────────── */
92
+ [data-image-generator] { border: 1px solid var(--gai-border); border-radius: var(--gai-radius); }
93
+ [data-image-gen-form] { padding: 1rem; display: flex; flex-direction: column; gap: 0.75rem; }
94
+ [data-image-gen-textarea] { padding: 0.75rem; border: 1px solid var(--gai-border); border-radius: var(--gai-radius-sm); background: var(--gai-bg); color: var(--gai-text); font-size: 0.875rem; resize: vertical; min-height: 80px; font-family: var(--gai-font); }
95
+ [data-image-gen-submit] { align-self: flex-start; padding: 0.625rem 1.5rem; background: var(--gai-primary); color: white; border: none; border-radius: var(--gai-radius-sm); font-weight: 600; cursor: pointer; }
96
+ [data-image-gen-gallery] { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.75rem; padding: 1rem; }
97
+
98
+ /* ─── Voice Selector ────────────────────────────── */
99
+ [data-voice-selector] { display: flex; flex-direction: column; gap: 0.5rem; }
100
+ [data-voice-option] { display: flex; align-items: center; gap: 0.75rem; padding: 0.625rem 1rem; border: 1px solid var(--gai-border); border-radius: var(--gai-radius-sm); cursor: pointer; transition: all var(--gai-transition); }
101
+ [data-voice-option]:hover { background: var(--gai-surface-hover); }
102
+ [data-voice-option][data-selected] { border-color: var(--gai-primary); background: var(--gai-primary-dim); }
103
+ [data-voice-name] { font-size: 0.875rem; font-weight: 600; }
104
+ [data-voice-preview] { margin-left: auto; padding: 0.375rem 0.75rem; border: 1px solid var(--gai-border); border-radius: 6px; background: transparent; color: var(--gai-text); font-size: 0.75rem; cursor: pointer; }
105
+
106
+ /* ─── Shared ─────────────────────────────────────── */
107
+ [data-ai-empty] { text-align: center; padding: 3rem 1rem; color: var(--gai-text-muted); }
108
+ [data-ai-empty-icon] { font-size: 2.5rem; margin-bottom: 0.5rem; }
109
+ [data-ai-error] { padding: 1rem; border: 1px solid var(--gai-error); border-radius: var(--gai-radius-sm); background: oklch(0.62 0.2 25 / 0.08); color: var(--gai-error); font-size: 0.875rem; }
110
+ [data-ai-loading] { display: flex; align-items: center; justify-content: center; padding: 2rem; color: var(--gai-text-muted); }
111
+ [data-ai-badge] { display: inline-flex; padding: 0.125rem 0.5rem; border-radius: 999px; font-size: 0.625rem; font-weight: 700; }
112
+ [data-ai-badge][data-variant="success"] { background: oklch(0.68 0.18 155 / 0.12); color: var(--gai-success); }
113
+ [data-ai-badge][data-variant="error"] { background: oklch(0.62 0.2 25 / 0.12); color: var(--gai-error); }
114
+ [data-ai-badge][data-variant="info"] { background: var(--gai-primary-dim); color: var(--gai-primary); }
115
+
116
+ /* ─── Pages ──────────────────────────────────────── */
117
+ [data-ai-page] { display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem; max-width: 1200px; margin: 0 auto; }
118
+ [data-ai-page-header] h1 { font-size: 1.5rem; font-weight: 800; letter-spacing: -0.02em; }
119
+ [data-ai-page-header] p { font-size: 0.875rem; color: var(--gai-text-muted); margin-top: 0.25rem; }
120
+
121
+ @keyframes gai-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
122
+
123
+ @media (max-width: 768px) {
124
+ [data-ai-log-header], [data-ai-log-row] { grid-template-columns: 1fr 1fr 0.5fr; }
125
+ [data-chat-message] { max-width: 90%; }
126
+ [data-image-gen-gallery] { grid-template-columns: 1fr; }
127
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "strict": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "resolveJsonModule": true,
16
+ "isolatedModules": true,
17
+ "jsx": "react-jsx"
18
+ },
19
+ "include": [
20
+ "src"
21
+ ],
22
+ "exclude": [
23
+ "node_modules",
24
+ "dist"
25
+ ]
26
+ }
@@ -0,0 +1,2 @@
1
+ import { defineConfig } from 'tsup'
2
+ export default defineConfig({ entry: ['src/index.ts'], format: ['cjs', 'esm'], dts: true, clean: true, sourcemap: true, external: ['react'] })
@@ -0,0 +1 @@
1
+ # ✦ @geenius-ai/shared\n\n> Framework-agnostic AI types, config, provider abstraction, and Convex backend for geenius-ai\n\n---\n\n## Overview\nBuilt with Steve Jobs-level minimalism and Jony Ive-level craftsmanship, this package is designed to deliver unparalleled developer experience (DX) and rock-solid performance.\n\n## Installation\n\n```bash\npnpm add @geenius-ai/shared\n```\n\n## Usage\n\n```typescript\nimport { init } from '@geenius-ai/shared';\n\n// Initialize the module with absolute precision\ninit({\n mode: 'premium',\n});\n```\n\n## Architecture\n- **Zero-config**: It just works.\n- **Strictly Typed**: Fully written in TypeScript for flawless IntelliSense.\n- **Framework Agnostic**: seamlessly integrates into the Geenius ecosystem.\n\n---\n\n*Designed by Antigravity HQ*\n
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@geenius-ai/shared",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Framework-agnostic AI types, config, provider abstraction, and Convex backend 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
+ "./config": {
18
+ "types": "./dist/config.d.ts",
19
+ "import": "./dist/config.js"
20
+ },
21
+ "./providers": {
22
+ "types": "./dist/providers/index.d.ts",
23
+ "import": "./dist/providers/index.js"
24
+ },
25
+ "./convex": {
26
+ "types": "./dist/convex/index.d.ts",
27
+ "import": "./dist/convex/index.js"
28
+ },
29
+ "./content": {
30
+ "types": "./dist/content.d.ts",
31
+ "import": "./dist/content.js"
32
+ },
33
+ "./memory": {
34
+ "types": "./dist/memory.d.ts",
35
+ "import": "./dist/memory.js"
36
+ },
37
+ "./skills": {
38
+ "types": "./dist/skills.d.ts",
39
+ "import": "./dist/skills.js"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "src"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "clean": "rm -rf dist",
49
+ "type-check": "tsc --noEmit",
50
+ "prepublishOnly": "pnpm clean && pnpm build",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "test:coverage": "vitest run --coverage"
54
+ },
55
+ "dependencies": {},
56
+ "peerDependencies": {
57
+ "convex": ">=1.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "convex": "^1.34.0",
61
+ "tsup": "^8.5.1",
62
+ "typescript": "~6.0.2",
63
+ "vitest": "^4.0.0"
64
+ },
65
+ "engines": {
66
+ "node": ">=20.0.0"
67
+ },
68
+ "publishConfig": {
69
+ "access": "public"
70
+ }
71
+ }