langwatch 0.1.7 → 0.3.0-prerelease.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.
Files changed (235) hide show
  1. package/.editorconfig +16 -0
  2. package/LICENSE +7 -0
  3. package/README.md +268 -1
  4. package/copy-types.sh +19 -8
  5. package/examples/langchain/.env.example +2 -0
  6. package/examples/langchain/README.md +42 -0
  7. package/examples/langchain/package-lock.json +2930 -0
  8. package/examples/langchain/package.json +27 -0
  9. package/examples/langchain/src/cli-markdown.d.ts +137 -0
  10. package/examples/langchain/src/index.ts +109 -0
  11. package/examples/langchain/tsconfig.json +25 -0
  12. package/examples/langgraph/.env.example +2 -0
  13. package/examples/langgraph/README.md +42 -0
  14. package/examples/langgraph/package-lock.json +3031 -0
  15. package/examples/langgraph/package.json +28 -0
  16. package/examples/langgraph/src/cli-markdown.d.ts +137 -0
  17. package/examples/langgraph/src/index.ts +196 -0
  18. package/examples/langgraph/tsconfig.json +25 -0
  19. package/examples/mastra/.env.example +2 -0
  20. package/examples/mastra/README.md +57 -0
  21. package/examples/mastra/package-lock.json +5296 -0
  22. package/examples/mastra/package.json +32 -0
  23. package/examples/mastra/src/cli-markdown.d.ts +137 -0
  24. package/examples/mastra/src/index.ts +120 -0
  25. package/examples/mastra/src/mastra/agents/weather-agent.ts +30 -0
  26. package/examples/mastra/src/mastra/index.ts +21 -0
  27. package/examples/mastra/src/mastra/tools/weather-tool.ts +102 -0
  28. package/examples/mastra/tsconfig.json +25 -0
  29. package/examples/vercel-ai/.env.example +2 -0
  30. package/examples/vercel-ai/README.md +38 -0
  31. package/examples/vercel-ai/package-lock.json +2571 -0
  32. package/examples/vercel-ai/package.json +27 -0
  33. package/examples/vercel-ai/src/cli-markdown.d.ts +137 -0
  34. package/examples/vercel-ai/src/index.ts +110 -0
  35. package/examples/vercel-ai/src/instrumentation.ts +9 -0
  36. package/examples/vercel-ai/tsconfig.json +25 -0
  37. package/package.json +80 -33
  38. package/src/__tests__/client-browser.test.ts +92 -0
  39. package/src/__tests__/client-node.test.ts +76 -0
  40. package/src/__tests__/client.test.ts +71 -0
  41. package/src/__tests__/integration/client-browser.test.ts +46 -0
  42. package/src/__tests__/integration/client-node.test.ts +46 -0
  43. package/src/client-browser.ts +70 -0
  44. package/src/client-node.ts +82 -0
  45. package/src/client-shared.ts +72 -0
  46. package/src/client.ts +119 -0
  47. package/src/evaluation/__tests__/record-evaluation.test.ts +112 -0
  48. package/src/evaluation/__tests__/run-evaluation.test.ts +171 -0
  49. package/src/evaluation/index.ts +2 -0
  50. package/src/evaluation/record-evaluation.ts +101 -0
  51. package/src/evaluation/run-evaluation.ts +133 -0
  52. package/src/evaluation/tracer.ts +3 -0
  53. package/src/evaluation/types.ts +23 -0
  54. package/src/index.ts +10 -591
  55. package/src/internal/api/__tests__/errors.test.ts +98 -0
  56. package/src/internal/api/client.ts +30 -0
  57. package/src/internal/api/errors.ts +32 -0
  58. package/src/internal/generated/types/.gitkeep +0 -0
  59. package/src/observability/__tests__/integration/base.test.ts +74 -0
  60. package/src/observability/__tests__/integration/browser-setup-ordering.test.ts +60 -0
  61. package/src/observability/__tests__/integration/complex-nested-spans.test.ts +29 -0
  62. package/src/observability/__tests__/integration/error-handling.test.ts +24 -0
  63. package/src/observability/__tests__/integration/langwatch-disabled-otel.test.ts +24 -0
  64. package/src/observability/__tests__/integration/langwatch-first-then-vercel.test.ts +24 -0
  65. package/src/observability/__tests__/integration/multiple-setup-attempts.test.ts +27 -0
  66. package/src/observability/__tests__/integration/otel-ordering.test.ts +27 -0
  67. package/src/observability/__tests__/integration/vercel-configurations.test.ts +20 -0
  68. package/src/observability/__tests__/integration/vercel-first-then-langwatch.test.ts +27 -0
  69. package/src/observability/__tests__/span.test.ts +214 -0
  70. package/src/observability/__tests__/trace.test.ts +180 -0
  71. package/src/observability/exporters/index.ts +1 -0
  72. package/src/observability/exporters/langwatch-exporter.ts +53 -0
  73. package/src/observability/index.ts +4 -0
  74. package/src/observability/instrumentation/langchain/__tests__/integration/langchain-chatbot.test.ts +112 -0
  75. package/src/observability/instrumentation/langchain/__tests__/langchain.test.ts +284 -0
  76. package/src/observability/instrumentation/langchain/index.ts +624 -0
  77. package/src/observability/processors/__tests__/filterable-batch-span-exporter.test.ts +98 -0
  78. package/src/observability/processors/filterable-batch-span-processor.ts +99 -0
  79. package/src/observability/processors/index.ts +1 -0
  80. package/src/observability/semconv/attributes.ts +185 -0
  81. package/src/observability/semconv/events.ts +42 -0
  82. package/src/observability/semconv/index.ts +16 -0
  83. package/src/observability/semconv/values.ts +159 -0
  84. package/src/observability/span.ts +728 -0
  85. package/src/observability/trace.ts +301 -0
  86. package/src/prompt/__tests__/prompt.test.ts +139 -0
  87. package/src/prompt/get-prompt-version.ts +49 -0
  88. package/src/prompt/get-prompt.ts +44 -0
  89. package/src/prompt/index.ts +3 -0
  90. package/src/prompt/prompt.ts +133 -0
  91. package/src/prompt/service.ts +221 -0
  92. package/src/prompt/tracer.ts +3 -0
  93. package/src/prompt/types.ts +0 -0
  94. package/ts-to-zod.config.js +11 -0
  95. package/tsconfig.json +3 -9
  96. package/tsup.config.ts +11 -1
  97. package/vitest.config.ts +1 -0
  98. package/dist/chunk-FWBCQQYZ.mjs +0 -711
  99. package/dist/chunk-FWBCQQYZ.mjs.map +0 -1
  100. package/dist/index.d.mts +0 -1010
  101. package/dist/index.d.ts +0 -1010
  102. package/dist/index.js +0 -27294
  103. package/dist/index.js.map +0 -1
  104. package/dist/index.mjs +0 -959
  105. package/dist/index.mjs.map +0 -1
  106. package/dist/utils-B0pgWcps.d.mts +0 -303
  107. package/dist/utils-B0pgWcps.d.ts +0 -303
  108. package/dist/utils.d.mts +0 -2
  109. package/dist/utils.d.ts +0 -2
  110. package/dist/utils.js +0 -703
  111. package/dist/utils.js.map +0 -1
  112. package/dist/utils.mjs +0 -11
  113. package/dist/utils.mjs.map +0 -1
  114. package/example/.env.example +0 -12
  115. package/example/.eslintrc.json +0 -26
  116. package/example/LICENSE +0 -13
  117. package/example/README.md +0 -12
  118. package/example/app/(chat)/chat/[id]/page.tsx +0 -60
  119. package/example/app/(chat)/layout.tsx +0 -14
  120. package/example/app/(chat)/page.tsx +0 -27
  121. package/example/app/actions.ts +0 -156
  122. package/example/app/globals.css +0 -76
  123. package/example/app/guardrails/page.tsx +0 -26
  124. package/example/app/langchain/page.tsx +0 -27
  125. package/example/app/langchain-rag/page.tsx +0 -28
  126. package/example/app/late-update/page.tsx +0 -27
  127. package/example/app/layout.tsx +0 -64
  128. package/example/app/login/actions.ts +0 -71
  129. package/example/app/login/page.tsx +0 -18
  130. package/example/app/manual/page.tsx +0 -27
  131. package/example/app/new/page.tsx +0 -5
  132. package/example/app/opengraph-image.png +0 -0
  133. package/example/app/share/[id]/page.tsx +0 -58
  134. package/example/app/signup/actions.ts +0 -111
  135. package/example/app/signup/page.tsx +0 -18
  136. package/example/app/twitter-image.png +0 -0
  137. package/example/auth.config.ts +0 -42
  138. package/example/auth.ts +0 -45
  139. package/example/components/button-scroll-to-bottom.tsx +0 -36
  140. package/example/components/chat-history.tsx +0 -49
  141. package/example/components/chat-list.tsx +0 -52
  142. package/example/components/chat-message-actions.tsx +0 -40
  143. package/example/components/chat-message.tsx +0 -80
  144. package/example/components/chat-panel.tsx +0 -139
  145. package/example/components/chat-share-dialog.tsx +0 -95
  146. package/example/components/chat.tsx +0 -84
  147. package/example/components/clear-history.tsx +0 -75
  148. package/example/components/empty-screen.tsx +0 -38
  149. package/example/components/external-link.tsx +0 -29
  150. package/example/components/footer.tsx +0 -19
  151. package/example/components/header.tsx +0 -114
  152. package/example/components/login-button.tsx +0 -42
  153. package/example/components/login-form.tsx +0 -97
  154. package/example/components/markdown.tsx +0 -9
  155. package/example/components/prompt-form.tsx +0 -115
  156. package/example/components/providers.tsx +0 -17
  157. package/example/components/sidebar-actions.tsx +0 -125
  158. package/example/components/sidebar-desktop.tsx +0 -19
  159. package/example/components/sidebar-footer.tsx +0 -16
  160. package/example/components/sidebar-item.tsx +0 -124
  161. package/example/components/sidebar-items.tsx +0 -42
  162. package/example/components/sidebar-list.tsx +0 -38
  163. package/example/components/sidebar-mobile.tsx +0 -31
  164. package/example/components/sidebar-toggle.tsx +0 -24
  165. package/example/components/sidebar.tsx +0 -21
  166. package/example/components/signup-form.tsx +0 -95
  167. package/example/components/stocks/events-skeleton.tsx +0 -31
  168. package/example/components/stocks/events.tsx +0 -30
  169. package/example/components/stocks/index.tsx +0 -36
  170. package/example/components/stocks/message.tsx +0 -134
  171. package/example/components/stocks/spinner.tsx +0 -16
  172. package/example/components/stocks/stock-purchase.tsx +0 -146
  173. package/example/components/stocks/stock-skeleton.tsx +0 -22
  174. package/example/components/stocks/stock.tsx +0 -210
  175. package/example/components/stocks/stocks-skeleton.tsx +0 -9
  176. package/example/components/stocks/stocks.tsx +0 -67
  177. package/example/components/tailwind-indicator.tsx +0 -14
  178. package/example/components/theme-toggle.tsx +0 -31
  179. package/example/components/ui/alert-dialog.tsx +0 -141
  180. package/example/components/ui/badge.tsx +0 -36
  181. package/example/components/ui/button.tsx +0 -57
  182. package/example/components/ui/codeblock.tsx +0 -148
  183. package/example/components/ui/dialog.tsx +0 -122
  184. package/example/components/ui/dropdown-menu.tsx +0 -205
  185. package/example/components/ui/icons.tsx +0 -507
  186. package/example/components/ui/input.tsx +0 -25
  187. package/example/components/ui/label.tsx +0 -26
  188. package/example/components/ui/select.tsx +0 -164
  189. package/example/components/ui/separator.tsx +0 -31
  190. package/example/components/ui/sheet.tsx +0 -140
  191. package/example/components/ui/sonner.tsx +0 -31
  192. package/example/components/ui/switch.tsx +0 -29
  193. package/example/components/ui/textarea.tsx +0 -24
  194. package/example/components/ui/tooltip.tsx +0 -30
  195. package/example/components/user-menu.tsx +0 -53
  196. package/example/components.json +0 -17
  197. package/example/instrumentation.ts +0 -11
  198. package/example/lib/chat/guardrails.tsx +0 -181
  199. package/example/lib/chat/langchain-rag.tsx +0 -191
  200. package/example/lib/chat/langchain.tsx +0 -112
  201. package/example/lib/chat/late-update.tsx +0 -208
  202. package/example/lib/chat/manual.tsx +0 -605
  203. package/example/lib/chat/vercel-ai.tsx +0 -576
  204. package/example/lib/hooks/use-copy-to-clipboard.tsx +0 -33
  205. package/example/lib/hooks/use-enter-submit.tsx +0 -23
  206. package/example/lib/hooks/use-local-storage.ts +0 -24
  207. package/example/lib/hooks/use-scroll-anchor.tsx +0 -86
  208. package/example/lib/hooks/use-sidebar.tsx +0 -60
  209. package/example/lib/hooks/use-streamable-text.ts +0 -25
  210. package/example/lib/types.ts +0 -41
  211. package/example/lib/utils.ts +0 -89
  212. package/example/middleware.ts +0 -8
  213. package/example/next-env.d.ts +0 -5
  214. package/example/next.config.js +0 -16
  215. package/example/package-lock.json +0 -9990
  216. package/example/package.json +0 -84
  217. package/example/pnpm-lock.yaml +0 -5712
  218. package/example/postcss.config.js +0 -6
  219. package/example/prettier.config.cjs +0 -34
  220. package/example/public/apple-touch-icon.png +0 -0
  221. package/example/public/favicon-16x16.png +0 -0
  222. package/example/public/favicon.ico +0 -0
  223. package/example/public/next.svg +0 -1
  224. package/example/public/thirteen.svg +0 -1
  225. package/example/public/vercel.svg +0 -1
  226. package/example/tailwind.config.ts +0 -81
  227. package/example/tsconfig.json +0 -35
  228. package/src/LangWatchExporter.ts +0 -91
  229. package/src/evaluations.ts +0 -219
  230. package/src/index.test.ts +0 -402
  231. package/src/langchain.ts +0 -557
  232. package/src/typeUtils.ts +0 -89
  233. package/src/types.ts +0 -79
  234. package/src/utils.ts +0 -205
  235. /package/src/{server/types → internal/generated/openapi}/.gitkeep +0 -0
@@ -1,42 +0,0 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import { signIn } from 'next-auth/react'
5
-
6
- import { cn } from '@/lib/utils'
7
- import { Button, type ButtonProps } from '@/components/ui/button'
8
- import { IconGitHub, IconSpinner } from '@/components/ui/icons'
9
-
10
- interface LoginButtonProps extends ButtonProps {
11
- showGithubIcon?: boolean
12
- text?: string
13
- }
14
-
15
- export function LoginButton({
16
- text = 'Login with GitHub',
17
- showGithubIcon = true,
18
- className,
19
- ...props
20
- }: LoginButtonProps) {
21
- const [isLoading, setIsLoading] = React.useState(false)
22
- return (
23
- <Button
24
- variant="outline"
25
- onClick={() => {
26
- setIsLoading(true)
27
- // next-auth signIn() function doesn't work yet at Edge Runtime due to usage of BroadcastChannel
28
- signIn('github', { callbackUrl: `/` })
29
- }}
30
- disabled={isLoading}
31
- className={cn(className)}
32
- {...props}
33
- >
34
- {isLoading ? (
35
- <IconSpinner className="mr-2 animate-spin" />
36
- ) : showGithubIcon ? (
37
- <IconGitHub className="mr-2" />
38
- ) : null}
39
- {text}
40
- </Button>
41
- )
42
- }
@@ -1,97 +0,0 @@
1
- 'use client'
2
-
3
- import { useFormState, useFormStatus } from 'react-dom'
4
- import { authenticate } from '@/app/login/actions'
5
- import Link from 'next/link'
6
- import { useEffect } from 'react'
7
- import { toast } from 'sonner'
8
- import { IconSpinner } from './ui/icons'
9
- import { getMessageFromCode } from '@/lib/utils'
10
- import { useRouter } from 'next/navigation'
11
-
12
- export default function LoginForm() {
13
- const router = useRouter()
14
- const [result, dispatch] = useFormState(authenticate, undefined)
15
-
16
- useEffect(() => {
17
- if (result) {
18
- if (result.type === 'error') {
19
- toast.error(getMessageFromCode(result.resultCode))
20
- } else {
21
- toast.success(getMessageFromCode(result.resultCode))
22
- router.refresh()
23
- }
24
- }
25
- }, [result, router])
26
-
27
- return (
28
- <form
29
- action={dispatch}
30
- className="flex flex-col items-center gap-4 space-y-3"
31
- >
32
- <div className="w-full flex-1 rounded-lg border bg-white px-6 pb-4 pt-8 shadow-md md:w-96 dark:bg-zinc-950">
33
- <h1 className="mb-3 text-2xl font-bold">Please log in to continue.</h1>
34
- <div className="w-full">
35
- <div>
36
- <label
37
- className="mb-3 mt-5 block text-xs font-medium text-zinc-400"
38
- htmlFor="email"
39
- >
40
- Email
41
- </label>
42
- <div className="relative">
43
- <input
44
- className="peer block w-full rounded-md border bg-zinc-50 px-2 py-[9px] text-sm outline-none placeholder:text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950"
45
- id="email"
46
- type="email"
47
- name="email"
48
- placeholder="Enter your email address"
49
- required
50
- />
51
- </div>
52
- </div>
53
- <div className="mt-4">
54
- <label
55
- className="mb-3 mt-5 block text-xs font-medium text-zinc-400"
56
- htmlFor="password"
57
- >
58
- Password
59
- </label>
60
- <div className="relative">
61
- <input
62
- className="peer block w-full rounded-md border bg-zinc-50 px-2 py-[9px] text-sm outline-none placeholder:text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950"
63
- id="password"
64
- type="password"
65
- name="password"
66
- placeholder="Enter password"
67
- required
68
- minLength={6}
69
- />
70
- </div>
71
- </div>
72
- </div>
73
- <LoginButton />
74
- </div>
75
-
76
- <Link
77
- href="/signup"
78
- className="flex flex-row gap-1 text-sm text-zinc-400"
79
- >
80
- No account yet? <div className="font-semibold underline">Sign up</div>
81
- </Link>
82
- </form>
83
- )
84
- }
85
-
86
- function LoginButton() {
87
- const { pending } = useFormStatus()
88
-
89
- return (
90
- <button
91
- className="my-4 flex h-10 w-full flex-row items-center justify-center rounded-md bg-zinc-900 p-2 text-sm font-semibold text-zinc-100 hover:bg-zinc-800 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-200"
92
- aria-disabled={pending}
93
- >
94
- {pending ? <IconSpinner /> : 'Log in'}
95
- </button>
96
- )
97
- }
@@ -1,9 +0,0 @@
1
- import { FC, memo } from 'react'
2
- import ReactMarkdown, { Options } from 'react-markdown'
3
-
4
- export const MemoizedReactMarkdown: FC<Options> = memo(
5
- ReactMarkdown,
6
- (prevProps, nextProps) =>
7
- prevProps.children === nextProps.children &&
8
- prevProps.className === nextProps.className
9
- )
@@ -1,115 +0,0 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import Textarea from 'react-textarea-autosize'
5
-
6
- import { useActions, useUIState } from 'ai/rsc'
7
-
8
- import { UserMessage } from './stocks/message'
9
- import { type AI } from '@/lib/chat/vercel-ai'
10
- import { Button } from '@/components/ui/button'
11
- import { IconArrowElbow, IconPlus } from '@/components/ui/icons'
12
- import {
13
- Tooltip,
14
- TooltipContent,
15
- TooltipTrigger
16
- } from '@/components/ui/tooltip'
17
- import { useEnterSubmit } from '@/lib/hooks/use-enter-submit'
18
- import { nanoid } from 'nanoid'
19
- import { useRouter } from 'next/navigation'
20
-
21
- export function PromptForm({
22
- input,
23
- setInput
24
- }: {
25
- input: string
26
- setInput: (value: string) => void
27
- }) {
28
- const router = useRouter()
29
- const { formRef, onKeyDown } = useEnterSubmit()
30
- const inputRef = React.useRef<HTMLTextAreaElement>(null)
31
- const { submitUserMessage } = useActions()
32
- const [_, setMessages] = useUIState<typeof AI>()
33
-
34
- React.useEffect(() => {
35
- if (inputRef.current) {
36
- inputRef.current.focus()
37
- }
38
- }, [])
39
-
40
- return (
41
- <form
42
- ref={formRef}
43
- onSubmit={async (e: any) => {
44
- e.preventDefault()
45
-
46
- // Blur focus on mobile
47
- if (window.innerWidth < 600) {
48
- e.target['message']?.blur()
49
- }
50
-
51
- const value = input.trim()
52
- setInput('')
53
- if (!value) return
54
-
55
- // Optimistically add user message UI
56
- setMessages(currentMessages => [
57
- ...currentMessages,
58
- {
59
- id: nanoid(),
60
- display: <UserMessage>{value}</UserMessage>
61
- }
62
- ])
63
-
64
- // Submit and get response message
65
- const responseMessage = await submitUserMessage(value)
66
- setMessages(currentMessages => [...currentMessages, responseMessage])
67
- }}
68
- >
69
- <div className="relative flex max-h-60 w-full grow flex-col overflow-hidden bg-background px-8 sm:rounded-md sm:border sm:px-12">
70
- <Tooltip>
71
- <TooltipTrigger asChild>
72
- <Button
73
- variant="outline"
74
- size="icon"
75
- className="absolute left-0 top-[14px] size-8 rounded-full bg-background p-0 sm:left-4"
76
- onClick={() => {
77
- router.push('/new')
78
- }}
79
- >
80
- <IconPlus />
81
- <span className="sr-only">New Chat</span>
82
- </Button>
83
- </TooltipTrigger>
84
- <TooltipContent>New Chat</TooltipContent>
85
- </Tooltip>
86
- <Textarea
87
- ref={inputRef}
88
- tabIndex={0}
89
- onKeyDown={onKeyDown}
90
- placeholder="Send a message."
91
- className="min-h-[60px] w-full resize-none bg-transparent px-4 py-[1.3rem] focus-within:outline-none sm:text-sm"
92
- autoFocus
93
- spellCheck={false}
94
- autoComplete="off"
95
- autoCorrect="off"
96
- name="message"
97
- rows={1}
98
- value={input}
99
- onChange={e => setInput(e.target.value)}
100
- />
101
- <div className="absolute right-0 top-[13px] sm:right-4">
102
- <Tooltip>
103
- <TooltipTrigger asChild>
104
- <Button type="submit" size="icon" disabled={input === ''}>
105
- <IconArrowElbow />
106
- <span className="sr-only">Send message</span>
107
- </Button>
108
- </TooltipTrigger>
109
- <TooltipContent>Send message</TooltipContent>
110
- </Tooltip>
111
- </div>
112
- </div>
113
- </form>
114
- )
115
- }
@@ -1,17 +0,0 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import { ThemeProvider as NextThemesProvider } from 'next-themes'
5
- import { ThemeProviderProps } from 'next-themes/dist/types'
6
- import { SidebarProvider } from '@/lib/hooks/use-sidebar'
7
- import { TooltipProvider } from '@/components/ui/tooltip'
8
-
9
- export function Providers({ children, ...props }: ThemeProviderProps) {
10
- return (
11
- <NextThemesProvider {...props}>
12
- <SidebarProvider>
13
- <TooltipProvider>{children}</TooltipProvider>
14
- </SidebarProvider>
15
- </NextThemesProvider>
16
- )
17
- }
@@ -1,125 +0,0 @@
1
- 'use client'
2
-
3
- import { useRouter } from 'next/navigation'
4
- import * as React from 'react'
5
- import { toast } from 'sonner'
6
-
7
- import { ServerActionResult, type Chat } from '@/lib/types'
8
- import {
9
- AlertDialog,
10
- AlertDialogAction,
11
- AlertDialogCancel,
12
- AlertDialogContent,
13
- AlertDialogDescription,
14
- AlertDialogFooter,
15
- AlertDialogHeader,
16
- AlertDialogTitle
17
- } from '@/components/ui/alert-dialog'
18
- import { Button } from '@/components/ui/button'
19
- import { IconShare, IconSpinner, IconTrash } from '@/components/ui/icons'
20
- import { ChatShareDialog } from '@/components/chat-share-dialog'
21
- import {
22
- Tooltip,
23
- TooltipContent,
24
- TooltipTrigger
25
- } from '@/components/ui/tooltip'
26
-
27
- interface SidebarActionsProps {
28
- chat: Chat
29
- removeChat: (args: { id: string; path: string }) => ServerActionResult<void>
30
- shareChat: (id: string) => ServerActionResult<Chat>
31
- }
32
-
33
- export function SidebarActions({
34
- chat,
35
- removeChat,
36
- shareChat
37
- }: SidebarActionsProps) {
38
- const router = useRouter()
39
- const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false)
40
- const [shareDialogOpen, setShareDialogOpen] = React.useState(false)
41
- const [isRemovePending, startRemoveTransition] = React.useTransition()
42
-
43
- return (
44
- <>
45
- <div className="">
46
- <Tooltip>
47
- <TooltipTrigger asChild>
48
- <Button
49
- variant="ghost"
50
- className="size-7 p-0 hover:bg-background"
51
- onClick={() => setShareDialogOpen(true)}
52
- >
53
- <IconShare />
54
- <span className="sr-only">Share</span>
55
- </Button>
56
- </TooltipTrigger>
57
- <TooltipContent>Share chat</TooltipContent>
58
- </Tooltip>
59
- <Tooltip>
60
- <TooltipTrigger asChild>
61
- <Button
62
- variant="ghost"
63
- className="size-7 p-0 hover:bg-background"
64
- disabled={isRemovePending}
65
- onClick={() => setDeleteDialogOpen(true)}
66
- >
67
- <IconTrash />
68
- <span className="sr-only">Delete</span>
69
- </Button>
70
- </TooltipTrigger>
71
- <TooltipContent>Delete chat</TooltipContent>
72
- </Tooltip>
73
- </div>
74
- <ChatShareDialog
75
- chat={chat}
76
- shareChat={shareChat}
77
- open={shareDialogOpen}
78
- onOpenChange={setShareDialogOpen}
79
- onCopy={() => setShareDialogOpen(false)}
80
- />
81
- <AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
82
- <AlertDialogContent>
83
- <AlertDialogHeader>
84
- <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
85
- <AlertDialogDescription>
86
- This will permanently delete your chat message and remove your
87
- data from our servers.
88
- </AlertDialogDescription>
89
- </AlertDialogHeader>
90
- <AlertDialogFooter>
91
- <AlertDialogCancel disabled={isRemovePending}>
92
- Cancel
93
- </AlertDialogCancel>
94
- <AlertDialogAction
95
- disabled={isRemovePending}
96
- onClick={event => {
97
- event.preventDefault()
98
- // @ts-ignore
99
- startRemoveTransition(async () => {
100
- const result = await removeChat({
101
- id: chat.id,
102
- path: chat.path
103
- })
104
-
105
- if (result && 'error' in result) {
106
- toast.error(result.error)
107
- return
108
- }
109
-
110
- setDeleteDialogOpen(false)
111
- router.refresh()
112
- router.push('/')
113
- toast.success('Chat deleted')
114
- })
115
- }}
116
- >
117
- {isRemovePending && <IconSpinner className="mr-2 animate-spin" />}
118
- Delete
119
- </AlertDialogAction>
120
- </AlertDialogFooter>
121
- </AlertDialogContent>
122
- </AlertDialog>
123
- </>
124
- )
125
- }
@@ -1,19 +0,0 @@
1
- import { Sidebar } from '@/components/sidebar'
2
-
3
- import { auth } from '@/auth'
4
- import { ChatHistory } from '@/components/chat-history'
5
-
6
- export async function SidebarDesktop() {
7
- const session = await auth()
8
-
9
- if (!session?.user?.id) {
10
- return null
11
- }
12
-
13
- return (
14
- <Sidebar className="peer absolute inset-y-0 z-30 hidden -translate-x-full border-r bg-muted duration-300 ease-in-out data-[state=open]:translate-x-0 lg:flex lg:w-[250px] xl:w-[300px]">
15
- {/* @ts-ignore */}
16
- <ChatHistory userId={session.user.id} />
17
- </Sidebar>
18
- )
19
- }
@@ -1,16 +0,0 @@
1
- import { cn } from '@/lib/utils'
2
-
3
- export function SidebarFooter({
4
- children,
5
- className,
6
- ...props
7
- }: React.ComponentProps<'div'>) {
8
- return (
9
- <div
10
- className={cn('flex items-center justify-between p-4', className)}
11
- {...props}
12
- >
13
- {children}
14
- </div>
15
- )
16
- }
@@ -1,124 +0,0 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
-
5
- import Link from 'next/link'
6
- import { usePathname } from 'next/navigation'
7
-
8
- import { motion } from 'framer-motion'
9
-
10
- import { buttonVariants } from '@/components/ui/button'
11
- import { IconMessage, IconUsers } from '@/components/ui/icons'
12
- import {
13
- Tooltip,
14
- TooltipContent,
15
- TooltipTrigger
16
- } from '@/components/ui/tooltip'
17
- import { useLocalStorage } from '@/lib/hooks/use-local-storage'
18
- import { type Chat } from '@/lib/types'
19
- import { cn } from '@/lib/utils'
20
-
21
- interface SidebarItemProps {
22
- index: number
23
- chat: Chat
24
- children: React.ReactNode
25
- }
26
-
27
- export function SidebarItem({ index, chat, children }: SidebarItemProps) {
28
- const pathname = usePathname()
29
-
30
- const isActive = pathname === chat.path
31
- const [newChatId, setNewChatId] = useLocalStorage('newChatId', null)
32
- const shouldAnimate = index === 0 && isActive && newChatId
33
-
34
- if (!chat?.id) return null
35
-
36
- return (
37
- <motion.div
38
- className="relative h-8"
39
- variants={{
40
- initial: {
41
- height: 0,
42
- opacity: 0
43
- },
44
- animate: {
45
- height: 'auto',
46
- opacity: 1
47
- }
48
- }}
49
- initial={shouldAnimate ? 'initial' : undefined}
50
- animate={shouldAnimate ? 'animate' : undefined}
51
- transition={{
52
- duration: 0.25,
53
- ease: 'easeIn'
54
- }}
55
- >
56
- <div className="absolute left-2 top-1 flex size-6 items-center justify-center">
57
- {chat.sharePath ? (
58
- <Tooltip delayDuration={1000}>
59
- <TooltipTrigger
60
- tabIndex={-1}
61
- className="focus:bg-muted focus:ring-1 focus:ring-ring"
62
- >
63
- <IconUsers className="mr-2 mt-1 text-zinc-500" />
64
- </TooltipTrigger>
65
- <TooltipContent>This is a shared chat.</TooltipContent>
66
- </Tooltip>
67
- ) : (
68
- <IconMessage className="mr-2 mt-1 text-zinc-500" />
69
- )}
70
- </div>
71
- <Link
72
- href={chat.path}
73
- className={cn(
74
- buttonVariants({ variant: 'ghost' }),
75
- 'group w-full px-8 transition-colors hover:bg-zinc-200/40 dark:hover:bg-zinc-300/10',
76
- isActive && 'bg-zinc-200 pr-16 font-semibold dark:bg-zinc-800'
77
- )}
78
- >
79
- <div
80
- className="relative max-h-5 flex-1 select-none overflow-hidden text-ellipsis break-all"
81
- title={chat.title}
82
- >
83
- <span className="whitespace-nowrap">
84
- {shouldAnimate ? (
85
- chat.title.split('').map((character, index) => (
86
- <motion.span
87
- key={index}
88
- variants={{
89
- initial: {
90
- opacity: 0,
91
- x: -100
92
- },
93
- animate: {
94
- opacity: 1,
95
- x: 0
96
- }
97
- }}
98
- initial={shouldAnimate ? 'initial' : undefined}
99
- animate={shouldAnimate ? 'animate' : undefined}
100
- transition={{
101
- duration: 0.25,
102
- ease: 'easeIn',
103
- delay: index * 0.05,
104
- staggerChildren: 0.05
105
- }}
106
- onAnimationComplete={() => {
107
- if (index === chat.title.length - 1) {
108
- setNewChatId(null)
109
- }
110
- }}
111
- >
112
- {character}
113
- </motion.span>
114
- ))
115
- ) : (
116
- <span>{chat.title}</span>
117
- )}
118
- </span>
119
- </div>
120
- </Link>
121
- {isActive && <div className="absolute right-2 top-1">{children}</div>}
122
- </motion.div>
123
- )
124
- }
@@ -1,42 +0,0 @@
1
- 'use client'
2
-
3
- import { Chat } from '@/lib/types'
4
- import { AnimatePresence, motion } from 'framer-motion'
5
-
6
- import { removeChat, shareChat } from '@/app/actions'
7
-
8
- import { SidebarActions } from '@/components/sidebar-actions'
9
- import { SidebarItem } from '@/components/sidebar-item'
10
-
11
- interface SidebarItemsProps {
12
- chats?: Chat[]
13
- }
14
-
15
- export function SidebarItems({ chats }: SidebarItemsProps) {
16
- if (!chats?.length) return null
17
-
18
- return (
19
- <AnimatePresence>
20
- {chats.map(
21
- (chat, index) =>
22
- chat && (
23
- <motion.div
24
- key={chat?.id}
25
- exit={{
26
- opacity: 0,
27
- height: 0
28
- }}
29
- >
30
- <SidebarItem index={index} chat={chat}>
31
- <SidebarActions
32
- chat={chat}
33
- removeChat={removeChat}
34
- shareChat={shareChat}
35
- />
36
- </SidebarItem>
37
- </motion.div>
38
- )
39
- )}
40
- </AnimatePresence>
41
- )
42
- }
@@ -1,38 +0,0 @@
1
- import { clearChats, getChats } from '@/app/actions'
2
- import { ClearHistory } from '@/components/clear-history'
3
- import { SidebarItems } from '@/components/sidebar-items'
4
- import { ThemeToggle } from '@/components/theme-toggle'
5
- import { cache } from 'react'
6
-
7
- interface SidebarListProps {
8
- userId?: string
9
- children?: React.ReactNode
10
- }
11
-
12
- const loadChats = cache(async (userId?: string) => {
13
- return await getChats(userId)
14
- })
15
-
16
- export async function SidebarList({ userId }: SidebarListProps) {
17
- const chats = await loadChats(userId)
18
-
19
- return (
20
- <div className="flex flex-1 flex-col overflow-hidden">
21
- <div className="flex-1 overflow-auto">
22
- {chats?.length ? (
23
- <div className="space-y-2 px-2">
24
- <SidebarItems chats={chats} />
25
- </div>
26
- ) : (
27
- <div className="p-8 text-center">
28
- <p className="text-sm text-muted-foreground">No chat history</p>
29
- </div>
30
- )}
31
- </div>
32
- <div className="flex items-center justify-between p-4">
33
- <ThemeToggle />
34
- <ClearHistory clearChats={clearChats} isEnabled={chats?.length > 0} />
35
- </div>
36
- </div>
37
- )
38
- }
@@ -1,31 +0,0 @@
1
- 'use client'
2
-
3
- import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
4
-
5
- import { Sidebar } from '@/components/sidebar'
6
- import { Button } from '@/components/ui/button'
7
-
8
- import { IconSidebar } from '@/components/ui/icons'
9
-
10
- interface SidebarMobileProps {
11
- children: React.ReactNode
12
- }
13
-
14
- export function SidebarMobile({ children }: SidebarMobileProps) {
15
- return (
16
- <Sheet>
17
- <SheetTrigger asChild>
18
- <Button variant="ghost" className="-ml-2 flex size-9 p-0 lg:hidden">
19
- <IconSidebar className="size-6" />
20
- <span className="sr-only">Toggle Sidebar</span>
21
- </Button>
22
- </SheetTrigger>
23
- <SheetContent
24
- side="left"
25
- className="inset-y-0 flex h-auto w-[300px] flex-col p-0"
26
- >
27
- <Sidebar className="flex">{children}</Sidebar>
28
- </SheetContent>
29
- </Sheet>
30
- )
31
- }