@hanzo/react 0.1.2 → 1.0.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 (52) hide show
  1. package/README.md +317 -53
  2. package/dist/index.d.mts +255 -0
  3. package/dist/index.d.ts +255 -0
  4. package/dist/index.js +548 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +539 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +84 -91
  9. package/src/components/HanzoProvider.test.tsx +346 -0
  10. package/src/components/HanzoProvider.tsx +508 -0
  11. package/src/hooks/index.ts +39 -0
  12. package/src/hooks/types.ts +162 -0
  13. package/src/hooks/useAuth.ts +0 -0
  14. package/src/hooks/useComponent.ts +0 -0
  15. package/src/hooks/useGenerativeUI.ts +0 -0
  16. package/src/hooks/useMCP.ts +0 -0
  17. package/src/hooks/useMessage.ts +105 -0
  18. package/src/hooks/useModelConfig.ts +0 -0
  19. package/src/hooks/useStreaming.ts +161 -0
  20. package/src/hooks/useSuggestions.ts +0 -0
  21. package/src/hooks/useThread.ts +0 -0
  22. package/src/hooks/useTool.ts +0 -0
  23. package/src/index.ts +40 -0
  24. package/src/types/index.ts +25 -0
  25. package/src/utils/cn.ts +6 -0
  26. package/src/utils/id.ts +6 -0
  27. package/src/utils/stream.ts +33 -0
  28. package/LICENSE +0 -21
  29. package/dist/index.cjs.js +0 -15755
  30. package/dist/index.cjs.js.map +0 -1
  31. package/dist/index.css +0 -789
  32. package/dist/index.esm.js +0 -15736
  33. package/dist/index.esm.js.map +0 -1
  34. package/dist/index.umd.js +0 -15756
  35. package/dist/index.umd.js.map +0 -1
  36. package/src/controls/.DS_Store +0 -0
  37. package/src/controls/MUICheckbox.js +0 -108
  38. package/src/controls/MUIKeyboardDatePicker.js +0 -90
  39. package/src/controls/MUIPhone.js +0 -33
  40. package/src/controls/MUISwitch.js +0 -107
  41. package/src/controls/MUIText.js +0 -298
  42. package/src/controls/NumericFormats.js +0 -57
  43. package/src/controls/control.js +0 -148
  44. package/src/controls/index.js +0 -7
  45. package/src/controls/material-ui-phone-number/components/Item.js +0 -66
  46. package/src/controls/material-ui-phone-number/components/flags.css +0 -789
  47. package/src/controls/material-ui-phone-number/components/index.js +0 -884
  48. package/src/controls/material-ui-phone-number/components/polyfills.js +0 -82
  49. package/src/controls/material-ui-phone-number/country_data.js +0 -1536
  50. package/src/controls/material-ui-phone-number/index.js +0 -3
  51. package/src/index.js +0 -1
  52. /package/src/{core/index.js → hooks/useAttachments.ts} +0 -0
@@ -0,0 +1,105 @@
1
+ "use client"
2
+
3
+ import { useState, useCallback, useRef } from 'react'
4
+ import { useHanzo } from '../components/HanzoProvider'
5
+ import type { Message } from '../components/HanzoProvider'
6
+
7
+ export interface UseMessageOptions {
8
+ threadId?: string
9
+ onSuccess?: (message: Message) => void
10
+ onError?: (error: Error) => void
11
+ autoRetry?: boolean
12
+ maxRetries?: number
13
+ retryDelay?: number
14
+ }
15
+
16
+ export interface UseMessageReturn {
17
+ sendMessage: (content: string) => Promise<Message>
18
+ sendMessageWithAttachments: (content: string, attachments: File[]) => Promise<Message>
19
+ isLoading: boolean
20
+ error: Error | null
21
+ lastMessage: Message | null
22
+ clearError: () => void
23
+ retry: () => Promise<void>
24
+ }
25
+
26
+ export function useMessage(options: UseMessageOptions = {}): UseMessageReturn {
27
+ const {
28
+ threadId,
29
+ onSuccess,
30
+ onError,
31
+ autoRetry = false,
32
+ maxRetries = 3,
33
+ retryDelay = 1000
34
+ } = options
35
+
36
+ const { sendMessage: sendToAPI, activeThreadId } = useHanzo()
37
+ const [isLoading, setIsLoading] = useState(false)
38
+ const [error, setError] = useState<Error | null>(null)
39
+ const [lastMessage, setLastMessage] = useState<Message | null>(null)
40
+ const lastContentRef = useRef<string>('')
41
+ const retryCountRef = useRef(0)
42
+
43
+ const sendMessage = useCallback(async (content: string): Promise<Message> => {
44
+ setIsLoading(true)
45
+ setError(null)
46
+ lastContentRef.current = content
47
+ retryCountRef.current = 0
48
+
49
+ try {
50
+ const targetThread = threadId || activeThreadId
51
+ if (!targetThread) {
52
+ throw new Error('No thread available')
53
+ }
54
+
55
+ const message = await sendToAPI(content, targetThread)
56
+ setLastMessage(message)
57
+ onSuccess?.(message)
58
+ return message
59
+ } catch (err) {
60
+ const error = err instanceof Error ? err : new Error('Failed to send message')
61
+ setError(error)
62
+ onError?.(error)
63
+
64
+ if (autoRetry && retryCountRef.current < maxRetries) {
65
+ retryCountRef.current++
66
+ setTimeout(() => retry(), retryDelay * retryCountRef.current)
67
+ }
68
+
69
+ throw error
70
+ } finally {
71
+ setIsLoading(false)
72
+ }
73
+ }, [sendToAPI, threadId, activeThreadId, onSuccess, onError, autoRetry, maxRetries, retryDelay])
74
+
75
+ const sendMessageWithAttachments = useCallback(async (
76
+ content: string,
77
+ attachments: File[]
78
+ ): Promise<Message> => {
79
+ // TODO: Implement file upload and attachment handling
80
+ // For now, we'll just append file names to the content
81
+ const attachmentInfo = attachments.map(f => `[Attached: ${f.name}]`).join(' ')
82
+ const enrichedContent = `${content}\n${attachmentInfo}`
83
+ return sendMessage(enrichedContent)
84
+ }, [sendMessage])
85
+
86
+ const clearError = useCallback(() => {
87
+ setError(null)
88
+ }, [])
89
+
90
+ const retry = useCallback(async () => {
91
+ if (lastContentRef.current) {
92
+ await sendMessage(lastContentRef.current)
93
+ }
94
+ }, [sendMessage])
95
+
96
+ return {
97
+ sendMessage,
98
+ sendMessageWithAttachments,
99
+ isLoading,
100
+ error,
101
+ lastMessage,
102
+ clearError,
103
+ retry
104
+ }
105
+ }
File without changes
@@ -0,0 +1,161 @@
1
+ "use client"
2
+
3
+ import { useState, useCallback, useRef, useEffect } from 'react'
4
+ import { useHanzo } from '../components/HanzoProvider'
5
+ import type { Message } from '../components/HanzoProvider'
6
+
7
+ export interface UseStreamingOptions {
8
+ threadId?: string
9
+ onChunk?: (chunk: string) => void
10
+ onComplete?: (message: Message) => void
11
+ onError?: (error: Error) => void
12
+ bufferSize?: number
13
+ throttleMs?: number
14
+ }
15
+
16
+ export interface UseStreamingReturn {
17
+ streamMessage: (content: string) => Promise<void>
18
+ isStreaming: boolean
19
+ currentMessage: string
20
+ error: Error | null
21
+ stopStreaming: () => void
22
+ clearMessage: () => void
23
+ progress: number // 0-100
24
+ }
25
+
26
+ export function useStreaming(options: UseStreamingOptions = {}): UseStreamingReturn {
27
+ const {
28
+ threadId,
29
+ onChunk,
30
+ onComplete,
31
+ onError,
32
+ bufferSize = 1,
33
+ throttleMs = 0
34
+ } = options
35
+
36
+ const { streamMessage: streamFromAPI, activeThreadId, isStreaming: globalStreaming } = useHanzo()
37
+ const [isStreaming, setIsStreaming] = useState(false)
38
+ const [currentMessage, setCurrentMessage] = useState('')
39
+ const [error, setError] = useState<Error | null>(null)
40
+ const [progress, setProgress] = useState(0)
41
+ const abortControllerRef = useRef<AbortController | null>(null)
42
+ const bufferRef = useRef<string[]>([])
43
+ const lastUpdateRef = useRef<number>(Date.now())
44
+
45
+ const streamMessage = useCallback(async (content: string): Promise<void> => {
46
+ setIsStreaming(true)
47
+ setError(null)
48
+ setCurrentMessage('')
49
+ setProgress(0)
50
+ bufferRef.current = []
51
+
52
+ // Create abort controller
53
+ abortControllerRef.current = new AbortController()
54
+
55
+ try {
56
+ const targetThread = threadId || activeThreadId
57
+ if (!targetThread) {
58
+ throw new Error('No thread available')
59
+ }
60
+
61
+ const stream = streamFromAPI(content, targetThread)
62
+ let totalChunks = 0
63
+ let estimatedTotal = 100 // Start with estimate, will adjust
64
+
65
+ for await (const message of stream) {
66
+ if (abortControllerRef.current?.signal.aborted) {
67
+ break
68
+ }
69
+
70
+ totalChunks++
71
+
72
+ // Extract string content from message
73
+ const chunk = typeof message.content === 'string' ? message.content : ''
74
+
75
+ // Buffer management
76
+ bufferRef.current.push(chunk)
77
+
78
+ // Throttling
79
+ const now = Date.now()
80
+ const shouldUpdate = (
81
+ bufferRef.current.length >= bufferSize ||
82
+ (throttleMs > 0 && now - lastUpdateRef.current >= throttleMs)
83
+ )
84
+
85
+ if (shouldUpdate) {
86
+ const bufferedContent = bufferRef.current.join('')
87
+ setCurrentMessage(prev => prev + bufferedContent)
88
+ onChunk?.(bufferedContent)
89
+ bufferRef.current = []
90
+ lastUpdateRef.current = now
91
+ }
92
+
93
+ // Update progress (estimate based on typical response length)
94
+ const estimatedProgress = Math.min(95, (totalChunks / estimatedTotal) * 100)
95
+ setProgress(estimatedProgress)
96
+
97
+ // Adjust estimate based on response speed
98
+ if (totalChunks > 10 && totalChunks % 10 === 0) {
99
+ estimatedTotal = Math.max(estimatedTotal, totalChunks * 1.5)
100
+ }
101
+ }
102
+
103
+ // Flush remaining buffer
104
+ if (bufferRef.current.length > 0) {
105
+ const remainingContent = bufferRef.current.join('')
106
+ setCurrentMessage(prev => prev + remainingContent)
107
+ onChunk?.(remainingContent)
108
+ bufferRef.current = []
109
+ }
110
+
111
+ setProgress(100)
112
+
113
+ // Create complete message
114
+ const completeMessage: Message = {
115
+ id: `stream-${Date.now()}`,
116
+ role: 'assistant',
117
+ content: currentMessage,
118
+ timestamp: new Date(),
119
+ threadId: targetThread
120
+ }
121
+
122
+ onComplete?.(completeMessage)
123
+ } catch (err) {
124
+ const error = err instanceof Error ? err : new Error('Streaming failed')
125
+ setError(error)
126
+ onError?.(error)
127
+ } finally {
128
+ setIsStreaming(false)
129
+ abortControllerRef.current = null
130
+ }
131
+ }, [streamFromAPI, threadId, activeThreadId, onChunk, onComplete, onError, bufferSize, throttleMs, currentMessage])
132
+
133
+ const stopStreaming = useCallback(() => {
134
+ if (abortControllerRef.current) {
135
+ abortControllerRef.current.abort()
136
+ setIsStreaming(false)
137
+ }
138
+ }, [])
139
+
140
+ const clearMessage = useCallback(() => {
141
+ setCurrentMessage('')
142
+ setProgress(0)
143
+ }, [])
144
+
145
+ // Sync with global streaming state
146
+ useEffect(() => {
147
+ if (!globalStreaming && isStreaming) {
148
+ setIsStreaming(false)
149
+ }
150
+ }, [globalStreaming, isStreaming])
151
+
152
+ return {
153
+ streamMessage,
154
+ isStreaming,
155
+ currentMessage,
156
+ error,
157
+ stopStreaming,
158
+ clearMessage,
159
+ progress
160
+ }
161
+ }
File without changes
File without changes
File without changes
package/src/index.ts ADDED
@@ -0,0 +1,40 @@
1
+ // @hanzo/react - React package for building AI-powered applications
2
+ // with generative UI and natural language interactions
3
+
4
+ // Core Provider and Context
5
+ export {
6
+ HanzoProvider,
7
+ useHanzo,
8
+ type HanzoProviderProps,
9
+ type HanzoContextValue,
10
+ type HanzoComponent,
11
+ type HanzoTool,
12
+ type Message,
13
+ type Thread,
14
+ type ToolCall
15
+ } from './components/HanzoProvider'
16
+
17
+ // Hooks
18
+ export { useMessage, type UseMessageOptions, type UseMessageReturn } from './hooks/useMessage'
19
+ export { useStreaming, type UseStreamingOptions, type UseStreamingReturn } from './hooks/useStreaming'
20
+ // TODO: Implement the following hooks
21
+ // export { useThread } from './hooks/useThread'
22
+ // export { useComponent } from './hooks/useComponent'
23
+ // export { useTool } from './hooks/useTool'
24
+ // export { useSuggestions } from './hooks/useSuggestions'
25
+ // export { useModelConfig } from './hooks/useModelConfig'
26
+ // export { useMCP } from './hooks/useMCP'
27
+ // export { useGenerativeUI } from './hooks/useGenerativeUI'
28
+ // export { useAuth } from './hooks/useAuth'
29
+ // export { useAttachments } from './hooks/useAttachments'
30
+
31
+ // Utilities
32
+ export { cn } from './utils/cn'
33
+ export { generateId } from './utils/id'
34
+ export { parseStream } from './utils/stream'
35
+
36
+ // Types
37
+ export * from './types'
38
+
39
+ // Version
40
+ export const VERSION = '1.0.0'
@@ -0,0 +1,25 @@
1
+ // Re-export all types from hooks
2
+ export * from '../hooks/types'
3
+
4
+ // Additional common types
5
+ export interface Suggestion {
6
+ id: string
7
+ text: string
8
+ metadata?: Record<string, any>
9
+ }
10
+
11
+ export interface Model {
12
+ id: string
13
+ name: string
14
+ description: string
15
+ parameters?: Record<string, any>
16
+ }
17
+
18
+ export interface Attachment {
19
+ id: string
20
+ name: string
21
+ size: number
22
+ type: string
23
+ url?: string
24
+ file?: File
25
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,6 @@
1
+ import { nanoid } from 'nanoid'
2
+
3
+ export function generateId(prefix?: string): string {
4
+ const id = nanoid()
5
+ return prefix ? `${prefix}-${id}` : id
6
+ }
@@ -0,0 +1,33 @@
1
+ export async function parseStream(stream: ReadableStream<Uint8Array>) {
2
+ const reader = stream.getReader()
3
+ const decoder = new TextDecoder()
4
+ const chunks: string[] = []
5
+
6
+ while (true) {
7
+ const { done, value } = await reader.read()
8
+ if (done) break
9
+
10
+ const chunk = decoder.decode(value)
11
+ chunks.push(chunk)
12
+ }
13
+
14
+ return chunks.join('')
15
+ }
16
+
17
+ export function* streamToGenerator<T>(stream: ReadableStream<T>) {
18
+ const reader = stream.getReader()
19
+
20
+ async function* generator() {
21
+ try {
22
+ while (true) {
23
+ const { done, value } = await reader.read()
24
+ if (done) break
25
+ yield value
26
+ }
27
+ } finally {
28
+ reader.releaseLock()
29
+ }
30
+ }
31
+
32
+ return generator()
33
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2018-present Someone
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.