langwatch 0.0.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/.eslintrc.cjs +37 -0
- package/README.md +3 -0
- package/dist/chunk-GOA2HL4A.mjs +269 -0
- package/dist/chunk-GOA2HL4A.mjs.map +1 -0
- package/dist/index.d.mts +82 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +940 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +666 -0
- package/dist/index.mjs.map +1 -0
- package/dist/utils-s3gGR6vj.d.mts +209 -0
- package/dist/utils-s3gGR6vj.d.ts +209 -0
- package/dist/utils.d.mts +3 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +263 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +7 -0
- package/dist/utils.mjs.map +1 -0
- package/example/.env.example +12 -0
- package/example/.eslintrc.json +26 -0
- package/example/LICENSE +13 -0
- package/example/README.md +10 -0
- package/example/app/(chat)/chat/[id]/page.tsx +60 -0
- package/example/app/(chat)/layout.tsx +14 -0
- package/example/app/(chat)/page.tsx +22 -0
- package/example/app/actions.ts +156 -0
- package/example/app/globals.css +76 -0
- package/example/app/layout.tsx +64 -0
- package/example/app/login/actions.ts +71 -0
- package/example/app/login/page.tsx +18 -0
- package/example/app/new/page.tsx +5 -0
- package/example/app/opengraph-image.png +0 -0
- package/example/app/share/[id]/page.tsx +58 -0
- package/example/app/signup/actions.ts +111 -0
- package/example/app/signup/page.tsx +18 -0
- package/example/app/twitter-image.png +0 -0
- package/example/auth.config.ts +42 -0
- package/example/auth.ts +45 -0
- package/example/components/button-scroll-to-bottom.tsx +36 -0
- package/example/components/chat-history.tsx +49 -0
- package/example/components/chat-list.tsx +52 -0
- package/example/components/chat-message-actions.tsx +40 -0
- package/example/components/chat-message.tsx +80 -0
- package/example/components/chat-panel.tsx +139 -0
- package/example/components/chat-share-dialog.tsx +95 -0
- package/example/components/chat.tsx +84 -0
- package/example/components/clear-history.tsx +75 -0
- package/example/components/empty-screen.tsx +38 -0
- package/example/components/external-link.tsx +29 -0
- package/example/components/footer.tsx +19 -0
- package/example/components/header.tsx +80 -0
- package/example/components/login-button.tsx +42 -0
- package/example/components/login-form.tsx +97 -0
- package/example/components/markdown.tsx +9 -0
- package/example/components/prompt-form.tsx +115 -0
- package/example/components/providers.tsx +17 -0
- package/example/components/sidebar-actions.tsx +125 -0
- package/example/components/sidebar-desktop.tsx +19 -0
- package/example/components/sidebar-footer.tsx +16 -0
- package/example/components/sidebar-item.tsx +124 -0
- package/example/components/sidebar-items.tsx +42 -0
- package/example/components/sidebar-list.tsx +38 -0
- package/example/components/sidebar-mobile.tsx +31 -0
- package/example/components/sidebar-toggle.tsx +24 -0
- package/example/components/sidebar.tsx +21 -0
- package/example/components/signup-form.tsx +95 -0
- package/example/components/stocks/events-skeleton.tsx +31 -0
- package/example/components/stocks/events.tsx +30 -0
- package/example/components/stocks/index.tsx +36 -0
- package/example/components/stocks/message.tsx +134 -0
- package/example/components/stocks/spinner.tsx +16 -0
- package/example/components/stocks/stock-purchase.tsx +146 -0
- package/example/components/stocks/stock-skeleton.tsx +22 -0
- package/example/components/stocks/stock.tsx +210 -0
- package/example/components/stocks/stocks-skeleton.tsx +9 -0
- package/example/components/stocks/stocks.tsx +67 -0
- package/example/components/tailwind-indicator.tsx +14 -0
- package/example/components/theme-toggle.tsx +31 -0
- package/example/components/ui/alert-dialog.tsx +141 -0
- package/example/components/ui/badge.tsx +36 -0
- package/example/components/ui/button.tsx +57 -0
- package/example/components/ui/codeblock.tsx +148 -0
- package/example/components/ui/dialog.tsx +122 -0
- package/example/components/ui/dropdown-menu.tsx +205 -0
- package/example/components/ui/icons.tsx +507 -0
- package/example/components/ui/input.tsx +25 -0
- package/example/components/ui/label.tsx +26 -0
- package/example/components/ui/select.tsx +164 -0
- package/example/components/ui/separator.tsx +31 -0
- package/example/components/ui/sheet.tsx +140 -0
- package/example/components/ui/sonner.tsx +31 -0
- package/example/components/ui/switch.tsx +29 -0
- package/example/components/ui/textarea.tsx +24 -0
- package/example/components/ui/tooltip.tsx +30 -0
- package/example/components/user-menu.tsx +53 -0
- package/example/components.json +17 -0
- package/example/lib/chat/actions.tsx +606 -0
- package/example/lib/hooks/use-copy-to-clipboard.tsx +33 -0
- package/example/lib/hooks/use-enter-submit.tsx +23 -0
- package/example/lib/hooks/use-local-storage.ts +24 -0
- package/example/lib/hooks/use-scroll-anchor.tsx +86 -0
- package/example/lib/hooks/use-sidebar.tsx +60 -0
- package/example/lib/hooks/use-streamable-text.ts +25 -0
- package/example/lib/types.ts +41 -0
- package/example/lib/utils.ts +89 -0
- package/example/middleware.ts +8 -0
- package/example/next-env.d.ts +5 -0
- package/example/next.config.js +13 -0
- package/example/package-lock.json +9249 -0
- package/example/package.json +77 -0
- package/example/pnpm-lock.yaml +5712 -0
- package/example/postcss.config.js +6 -0
- package/example/prettier.config.cjs +34 -0
- package/example/public/apple-touch-icon.png +0 -0
- package/example/public/favicon-16x16.png +0 -0
- package/example/public/favicon.ico +0 -0
- package/example/public/next.svg +1 -0
- package/example/public/thirteen.svg +1 -0
- package/example/public/vercel.svg +1 -0
- package/example/tailwind.config.ts +81 -0
- package/example/tsconfig.json +35 -0
- package/package.json +45 -0
- package/src/helpers.ts +64 -0
- package/src/index.test.ts +255 -0
- package/src/index.ts +397 -0
- package/src/server/types/.gitkeep +0 -0
- package/src/types.ts +69 -0
- package/src/utils.ts +134 -0
- package/ts-to-zod.config.js +18 -0
- package/tsconfig.json +32 -0
- package/tsup.config.ts +10 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export const useScrollAnchor = () => {
|
|
4
|
+
const messagesRef = useRef<HTMLDivElement>(null)
|
|
5
|
+
const scrollRef = useRef<HTMLDivElement>(null)
|
|
6
|
+
const visibilityRef = useRef<HTMLDivElement>(null)
|
|
7
|
+
|
|
8
|
+
const [isAtBottom, setIsAtBottom] = useState(true)
|
|
9
|
+
const [isVisible, setIsVisible] = useState(false)
|
|
10
|
+
|
|
11
|
+
const scrollToBottom = useCallback(() => {
|
|
12
|
+
if (messagesRef.current) {
|
|
13
|
+
messagesRef.current.scrollIntoView({
|
|
14
|
+
block: 'end',
|
|
15
|
+
behavior: 'smooth'
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
}, [])
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (messagesRef.current) {
|
|
22
|
+
if (isAtBottom && !isVisible) {
|
|
23
|
+
messagesRef.current.scrollIntoView({
|
|
24
|
+
block: 'end'
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}, [isAtBottom, isVisible])
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const { current } = scrollRef
|
|
32
|
+
|
|
33
|
+
if (current) {
|
|
34
|
+
const handleScroll = (event: Event) => {
|
|
35
|
+
const target = event.target as HTMLDivElement
|
|
36
|
+
const offset = 25
|
|
37
|
+
const isAtBottom =
|
|
38
|
+
target.scrollTop + target.clientHeight >= target.scrollHeight - offset
|
|
39
|
+
|
|
40
|
+
setIsAtBottom(isAtBottom)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
current.addEventListener('scroll', handleScroll, {
|
|
44
|
+
passive: true
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return () => {
|
|
48
|
+
current.removeEventListener('scroll', handleScroll)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}, [])
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (visibilityRef.current) {
|
|
55
|
+
let observer = new IntersectionObserver(
|
|
56
|
+
entries => {
|
|
57
|
+
entries.forEach(entry => {
|
|
58
|
+
if (entry.isIntersecting) {
|
|
59
|
+
setIsVisible(true)
|
|
60
|
+
} else {
|
|
61
|
+
setIsVisible(false)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
rootMargin: '0px 0px -150px 0px'
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
observer.observe(visibilityRef.current)
|
|
71
|
+
|
|
72
|
+
return () => {
|
|
73
|
+
observer.disconnect()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
messagesRef,
|
|
80
|
+
scrollRef,
|
|
81
|
+
visibilityRef,
|
|
82
|
+
scrollToBottom,
|
|
83
|
+
isAtBottom,
|
|
84
|
+
isVisible
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
const LOCAL_STORAGE_KEY = 'sidebar'
|
|
6
|
+
|
|
7
|
+
interface SidebarContext {
|
|
8
|
+
isSidebarOpen: boolean
|
|
9
|
+
toggleSidebar: () => void
|
|
10
|
+
isLoading: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const SidebarContext = React.createContext<SidebarContext | undefined>(
|
|
14
|
+
undefined
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
export function useSidebar() {
|
|
18
|
+
const context = React.useContext(SidebarContext)
|
|
19
|
+
if (!context) {
|
|
20
|
+
throw new Error('useSidebarContext must be used within a SidebarProvider')
|
|
21
|
+
}
|
|
22
|
+
return context
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface SidebarProviderProps {
|
|
26
|
+
children: React.ReactNode
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function SidebarProvider({ children }: SidebarProviderProps) {
|
|
30
|
+
const [isSidebarOpen, setSidebarOpen] = React.useState(true)
|
|
31
|
+
const [isLoading, setLoading] = React.useState(true)
|
|
32
|
+
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
const value = localStorage.getItem(LOCAL_STORAGE_KEY)
|
|
35
|
+
if (value) {
|
|
36
|
+
setSidebarOpen(JSON.parse(value))
|
|
37
|
+
}
|
|
38
|
+
setLoading(false)
|
|
39
|
+
}, [])
|
|
40
|
+
|
|
41
|
+
const toggleSidebar = () => {
|
|
42
|
+
setSidebarOpen(value => {
|
|
43
|
+
const newState = !value
|
|
44
|
+
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newState))
|
|
45
|
+
return newState
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (isLoading) {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<SidebarContext.Provider
|
|
55
|
+
value={{ isSidebarOpen, toggleSidebar, isLoading }}
|
|
56
|
+
>
|
|
57
|
+
{children}
|
|
58
|
+
</SidebarContext.Provider>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StreamableValue, readStreamableValue } from 'ai/rsc'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
3
|
+
|
|
4
|
+
export const useStreamableText = (
|
|
5
|
+
content: string | StreamableValue<string>
|
|
6
|
+
) => {
|
|
7
|
+
const [rawContent, setRawContent] = useState(
|
|
8
|
+
typeof content === 'string' ? content : ''
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
;(async () => {
|
|
13
|
+
if (typeof content === 'object') {
|
|
14
|
+
let value = ''
|
|
15
|
+
for await (const delta of readStreamableValue(content)) {
|
|
16
|
+
if (typeof delta === 'string') {
|
|
17
|
+
setRawContent((value = value + delta))
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
})()
|
|
22
|
+
}, [content])
|
|
23
|
+
|
|
24
|
+
return rawContent
|
|
25
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { CoreMessage } from 'ai'
|
|
2
|
+
|
|
3
|
+
export type Message = CoreMessage & {
|
|
4
|
+
id: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface Chat extends Record<string, any> {
|
|
8
|
+
id: string
|
|
9
|
+
title: string
|
|
10
|
+
createdAt: Date
|
|
11
|
+
userId: string
|
|
12
|
+
path: string
|
|
13
|
+
messages: Message[]
|
|
14
|
+
sharePath?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ServerActionResult<Result> = Promise<
|
|
18
|
+
| Result
|
|
19
|
+
| {
|
|
20
|
+
error: string
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
|
|
24
|
+
export interface Session {
|
|
25
|
+
user: {
|
|
26
|
+
id: string
|
|
27
|
+
email: string
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AuthResult {
|
|
32
|
+
type: string
|
|
33
|
+
message: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface User extends Record<string, any> {
|
|
37
|
+
id: string
|
|
38
|
+
email: string
|
|
39
|
+
password: string
|
|
40
|
+
salt: string
|
|
41
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { clsx, type ClassValue } from 'clsx'
|
|
2
|
+
import { customAlphabet } from 'nanoid'
|
|
3
|
+
import { twMerge } from 'tailwind-merge'
|
|
4
|
+
|
|
5
|
+
export function cn(...inputs: ClassValue[]) {
|
|
6
|
+
return twMerge(clsx(inputs))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const nanoid = customAlphabet(
|
|
10
|
+
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
|
|
11
|
+
7
|
|
12
|
+
) // 7-character random string
|
|
13
|
+
|
|
14
|
+
export async function fetcher<JSON = any>(
|
|
15
|
+
input: RequestInfo,
|
|
16
|
+
init?: RequestInit
|
|
17
|
+
): Promise<JSON> {
|
|
18
|
+
const res = await fetch(input, init)
|
|
19
|
+
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
const json = await res.json()
|
|
22
|
+
if (json.error) {
|
|
23
|
+
const error = new Error(json.error) as Error & {
|
|
24
|
+
status: number
|
|
25
|
+
}
|
|
26
|
+
error.status = res.status
|
|
27
|
+
throw error
|
|
28
|
+
} else {
|
|
29
|
+
throw new Error('An unexpected error occurred')
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return res.json()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function formatDate(input: string | number | Date): string {
|
|
37
|
+
const date = new Date(input)
|
|
38
|
+
return date.toLocaleDateString('en-US', {
|
|
39
|
+
month: 'long',
|
|
40
|
+
day: 'numeric',
|
|
41
|
+
year: 'numeric'
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const formatNumber = (value: number) =>
|
|
46
|
+
new Intl.NumberFormat('en-US', {
|
|
47
|
+
style: 'currency',
|
|
48
|
+
currency: 'USD'
|
|
49
|
+
}).format(value)
|
|
50
|
+
|
|
51
|
+
export const runAsyncFnWithoutBlocking = (
|
|
52
|
+
fn: (...args: any) => Promise<any>
|
|
53
|
+
) => {
|
|
54
|
+
fn()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const sleep = (ms: number) =>
|
|
58
|
+
new Promise(resolve => setTimeout(resolve, ms))
|
|
59
|
+
|
|
60
|
+
export const getStringFromBuffer = (buffer: ArrayBuffer) =>
|
|
61
|
+
Array.from(new Uint8Array(buffer))
|
|
62
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
63
|
+
.join('')
|
|
64
|
+
|
|
65
|
+
export enum ResultCode {
|
|
66
|
+
InvalidCredentials = 'INVALID_CREDENTIALS',
|
|
67
|
+
InvalidSubmission = 'INVALID_SUBMISSION',
|
|
68
|
+
UserAlreadyExists = 'USER_ALREADY_EXISTS',
|
|
69
|
+
UnknownError = 'UNKNOWN_ERROR',
|
|
70
|
+
UserCreated = 'USER_CREATED',
|
|
71
|
+
UserLoggedIn = 'USER_LOGGED_IN'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const getMessageFromCode = (resultCode: string) => {
|
|
75
|
+
switch (resultCode) {
|
|
76
|
+
case ResultCode.InvalidCredentials:
|
|
77
|
+
return 'Invalid credentials!'
|
|
78
|
+
case ResultCode.InvalidSubmission:
|
|
79
|
+
return 'Invalid submission, please try again!'
|
|
80
|
+
case ResultCode.UserAlreadyExists:
|
|
81
|
+
return 'User already exists, please log in!'
|
|
82
|
+
case ResultCode.UserCreated:
|
|
83
|
+
return 'User created, welcome!'
|
|
84
|
+
case ResultCode.UnknownError:
|
|
85
|
+
return 'Something went wrong, please try again!'
|
|
86
|
+
case ResultCode.UserLoggedIn:
|
|
87
|
+
return 'Logged in!'
|
|
88
|
+
}
|
|
89
|
+
}
|