viktor-hu commited on
Commit
34c5f27
·
1 Parent(s): 8e36730

feat. integrate posthog

Browse files
package.json CHANGED
@@ -49,6 +49,7 @@
49
  "lucide-react": "^0.454.0",
50
  "next": "15.2.4",
51
  "next-themes": "^0.4.4",
 
52
  "react": "^19",
53
  "react-day-picker": "8.10.1",
54
  "react-dom": "^19",
 
49
  "lucide-react": "^0.454.0",
50
  "next": "15.2.4",
51
  "next-themes": "^0.4.4",
52
+ "posthog-js": "^1.249.2",
53
  "react": "^19",
54
  "react-day-picker": "8.10.1",
55
  "react-dom": "^19",
pnpm-lock.yaml CHANGED
@@ -128,6 +128,9 @@ importers:
128
  next-themes:
129
  specifier: ^0.4.4
130
  version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
 
 
 
131
  react:
132
  specifier: ^19
133
  version: 19.1.0
@@ -1400,6 +1403,9 @@ packages:
1400
  resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
1401
  engines: {node: '>= 6'}
1402
 
 
 
 
1403
  cross-spawn@7.0.6:
1404
  resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
1405
  engines: {node: '>= 8'}
@@ -1521,6 +1527,9 @@ packages:
1521
  fastq@1.19.1:
1522
  resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
1523
 
 
 
 
1524
  fill-range@7.1.1:
1525
  resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
1526
  engines: {node: '>=8'}
@@ -1776,6 +1785,20 @@ packages:
1776
  resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
1777
  engines: {node: ^10 || ^12 || >=14}
1778
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1779
  prop-types@15.8.1:
1780
  resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
1781
 
@@ -2049,6 +2072,9 @@ packages:
2049
  victory-vendor@36.9.2:
2050
  resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
2051
 
 
 
 
2052
  which@2.0.2:
2053
  resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
2054
  engines: {node: '>= 8'}
@@ -3243,6 +3269,8 @@ snapshots:
3243
 
3244
  commander@4.1.1: {}
3245
 
 
 
3246
  cross-spawn@7.0.6:
3247
  dependencies:
3248
  path-key: 3.1.1
@@ -3347,6 +3375,8 @@ snapshots:
3347
  dependencies:
3348
  reusify: 1.1.0
3349
 
 
 
3350
  fill-range@7.1.1:
3351
  dependencies:
3352
  to-regex-range: 5.0.1
@@ -3565,6 +3595,15 @@ snapshots:
3565
  picocolors: 1.1.1
3566
  source-map-js: 1.2.1
3567
 
 
 
 
 
 
 
 
 
 
3568
  prop-types@15.8.1:
3569
  dependencies:
3570
  loose-envify: 1.4.0
@@ -3878,6 +3917,8 @@ snapshots:
3878
  d3-time: 3.1.0
3879
  d3-timer: 3.0.1
3880
 
 
 
3881
  which@2.0.2:
3882
  dependencies:
3883
  isexe: 2.0.0
 
128
  next-themes:
129
  specifier: ^0.4.4
130
  version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
131
+ posthog-js:
132
+ specifier: ^1.249.2
133
+ version: 1.249.2
134
  react:
135
  specifier: ^19
136
  version: 19.1.0
 
1403
  resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
1404
  engines: {node: '>= 6'}
1405
 
1406
+ core-js@3.42.0:
1407
+ resolution: {integrity: sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==}
1408
+
1409
  cross-spawn@7.0.6:
1410
  resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
1411
  engines: {node: '>= 8'}
 
1527
  fastq@1.19.1:
1528
  resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
1529
 
1530
+ fflate@0.4.8:
1531
+ resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
1532
+
1533
  fill-range@7.1.1:
1534
  resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
1535
  engines: {node: '>=8'}
 
1785
  resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
1786
  engines: {node: ^10 || ^12 || >=14}
1787
 
1788
+ posthog-js@1.249.2:
1789
+ resolution: {integrity: sha512-OMXCO/IfcJBjYTuebVynMbp8Kq329yKEQSCAnkqLmi8W2Bt5bi7S5xxMwDM3Pm7818Uh0C40XMG3rAtYozId6Q==}
1790
+ peerDependencies:
1791
+ '@rrweb/types': 2.0.0-alpha.17
1792
+ rrweb-snapshot: 2.0.0-alpha.17
1793
+ peerDependenciesMeta:
1794
+ '@rrweb/types':
1795
+ optional: true
1796
+ rrweb-snapshot:
1797
+ optional: true
1798
+
1799
+ preact@10.26.8:
1800
+ resolution: {integrity: sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==}
1801
+
1802
  prop-types@15.8.1:
1803
  resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
1804
 
 
2072
  victory-vendor@36.9.2:
2073
  resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
2074
 
2075
+ web-vitals@4.2.4:
2076
+ resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
2077
+
2078
  which@2.0.2:
2079
  resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
2080
  engines: {node: '>= 8'}
 
3269
 
3270
  commander@4.1.1: {}
3271
 
3272
+ core-js@3.42.0: {}
3273
+
3274
  cross-spawn@7.0.6:
3275
  dependencies:
3276
  path-key: 3.1.1
 
3375
  dependencies:
3376
  reusify: 1.1.0
3377
 
3378
+ fflate@0.4.8: {}
3379
+
3380
  fill-range@7.1.1:
3381
  dependencies:
3382
  to-regex-range: 5.0.1
 
3595
  picocolors: 1.1.1
3596
  source-map-js: 1.2.1
3597
 
3598
+ posthog-js@1.249.2:
3599
+ dependencies:
3600
+ core-js: 3.42.0
3601
+ fflate: 0.4.8
3602
+ preact: 10.26.8
3603
+ web-vitals: 4.2.4
3604
+
3605
+ preact@10.26.8: {}
3606
+
3607
  prop-types@15.8.1:
3608
  dependencies:
3609
  loose-envify: 1.4.0
 
3917
  d3-time: 3.1.0
3918
  d3-timer: 3.0.1
3919
 
3920
+ web-vitals@4.2.4: {}
3921
+
3922
  which@2.0.2:
3923
  dependencies:
3924
  isexe: 2.0.0
src/app/layout.tsx CHANGED
@@ -6,6 +6,7 @@ import { ThemeProvider } from "@/components/theme-provider"
6
  import { TooltipProvider } from "@/components/ui/tooltip"
7
  import { Toaster } from "sonner"
8
  import { ModelProvider } from "@/lib/contexts/model-context"
 
9
 
10
  const inter = Inter({ subsets: ["latin"] })
11
 
@@ -22,12 +23,14 @@ export default function RootLayout({
22
  return (
23
  <html lang="en" suppressHydrationWarning>
24
  <body className={inter.className} suppressHydrationWarning>
25
- <ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
26
- <ModelProvider>
27
- <TooltipProvider>{children}</TooltipProvider>
28
- <Toaster richColors />
29
- </ModelProvider>
30
- </ThemeProvider>
 
 
31
  </body>
32
  </html>
33
  )
 
6
  import { TooltipProvider } from "@/components/ui/tooltip"
7
  import { Toaster } from "sonner"
8
  import { ModelProvider } from "@/lib/contexts/model-context"
9
+ import { PostHogProvider } from './providers'
10
 
11
  const inter = Inter({ subsets: ["latin"] })
12
 
 
23
  return (
24
  <html lang="en" suppressHydrationWarning>
25
  <body className={inter.className} suppressHydrationWarning>
26
+ <PostHogProvider>
27
+ <ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
28
+ <ModelProvider>
29
+ <TooltipProvider>{children}</TooltipProvider>
30
+ <Toaster richColors />
31
+ </ModelProvider>
32
+ </ThemeProvider>
33
+ </PostHogProvider>
34
  </body>
35
  </html>
36
  )
src/app/providers.tsx ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // app/providers.tsx
2
+ 'use client'
3
+
4
+ import posthog from 'posthog-js'
5
+ import { PostHogProvider as PHProvider } from 'posthog-js/react'
6
+ import { useEffect } from 'react'
7
+
8
+ export function PostHogProvider({ children }: { children: React.ReactNode }) {
9
+ useEffect(() => {
10
+ posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY || '', {
11
+ api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
12
+ defaults: '2025-05-24',
13
+ })
14
+ }, [])
15
+
16
+ return (
17
+ <PHProvider client={posthog}>
18
+ {children}
19
+ </PHProvider>
20
+ )
21
+ }
src/components/preview.tsx CHANGED
@@ -7,6 +7,7 @@ import { MinimizeIcon, MaximizeIcon, DownloadIcon, RefreshIcon } from "./ui/icon
7
  import { useModel } from "@/lib/contexts/model-context"
8
  import { Loader2 } from "lucide-react"
9
  import { cn } from "@/lib/utils"
 
10
 
11
  interface PreviewProps {
12
  initialHtml?: string;
@@ -132,6 +133,8 @@ export const Preview = forwardRef<PreviewRef, PreviewProps>(function Preview(
132
  // Only include html in the request if it's not DEFAULT_HTML
133
  const isDefaultHtml = initialHtml === DEFAULT_HTML;
134
 
 
 
135
  const response = await fetch('/api/generate-code', {
136
  method: 'POST',
137
  headers: {
@@ -147,6 +150,7 @@ export const Preview = forwardRef<PreviewRef, PreviewProps>(function Preview(
147
  });
148
 
149
  if (!response.ok) {
 
150
  // Check specifically for 401 error (authentication required)
151
  if (response.status === 401 || response.status === 403) {
152
  try {
@@ -244,6 +248,7 @@ export const Preview = forwardRef<PreviewRef, PreviewProps>(function Preview(
244
  }
245
  } catch (err) {
246
  const errorMessage = (err as Error).message || 'An error occurred while generating code';
 
247
  setError(errorMessage);
248
  if (onErrorChange) {
249
  onErrorChange(errorMessage);
 
7
  import { useModel } from "@/lib/contexts/model-context"
8
  import { Loader2 } from "lucide-react"
9
  import { cn } from "@/lib/utils"
10
+ import posthog from 'posthog-js'
11
 
12
  interface PreviewProps {
13
  initialHtml?: string;
 
133
  // Only include html in the request if it's not DEFAULT_HTML
134
  const isDefaultHtml = initialHtml === DEFAULT_HTML;
135
 
136
+ posthog.capture("Generate code", {"model": selectedModelId});
137
+
138
  const response = await fetch('/api/generate-code', {
139
  method: 'POST',
140
  headers: {
 
150
  });
151
 
152
  if (!response.ok) {
153
+ posthog.capture("Generate code", {"type": "failed", "model": selectedModelId, "status": response.status});
154
  // Check specifically for 401 error (authentication required)
155
  if (response.status === 401 || response.status === 403) {
156
  try {
 
248
  }
249
  } catch (err) {
250
  const errorMessage = (err as Error).message || 'An error occurred while generating code';
251
+ posthog.capture("Generate code", {"type": "failed", "model": selectedModelId, "error": errorMessage});
252
  setError(errorMessage);
253
  if (onErrorChange) {
254
  onErrorChange(errorMessage);
src/components/prompt-input.tsx CHANGED
@@ -11,6 +11,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp
11
  import { FullscreenToggle } from "./ui/fullscreen-toggle"
12
  import { AuthErrorPopup } from "./auth-error-popup"
13
  import { getInferenceToken } from "@/lib/auth"
 
14
 
15
  interface PromptInputProps {
16
  onSubmit: (prompt: string, colors: string[]) => Promise<void>;
@@ -74,6 +75,8 @@ export function PromptInput({
74
  const isAuthenticated = await checkAuth();
75
  if (!isAuthenticated) return;
76
 
 
 
77
  // Clear previous errors
78
  setImproveError(null);
79
  setShowAuthError(false);
@@ -89,6 +92,8 @@ export function PromptInput({
89
  })
90
 
91
  if (!response.ok) {
 
 
92
  // Handle auth error with openLogin flag
93
  if (response.status === 401) {
94
  const errorData = await response.json();
@@ -135,6 +140,7 @@ export function PromptInput({
135
  }
136
  }
137
  } catch (error) {
 
138
  console.error("Error improving prompt:", error)
139
  setImproveError(error instanceof Error ? error.message : "Failed to improve prompt")
140
  } finally {
 
11
  import { FullscreenToggle } from "./ui/fullscreen-toggle"
12
  import { AuthErrorPopup } from "./auth-error-popup"
13
  import { getInferenceToken } from "@/lib/auth"
14
+ import posthog from 'posthog-js'
15
 
16
  interface PromptInputProps {
17
  onSubmit: (prompt: string, colors: string[]) => Promise<void>;
 
75
  const isAuthenticated = await checkAuth();
76
  if (!isAuthenticated) return;
77
 
78
+ posthog.capture("Improve prompt", {});
79
+
80
  // Clear previous errors
81
  setImproveError(null);
82
  setShowAuthError(false);
 
92
  })
93
 
94
  if (!response.ok) {
95
+ posthog.capture("Improve prompt", {"type": "failed", "status": response.status});
96
+
97
  // Handle auth error with openLogin flag
98
  if (response.status === 401) {
99
  const errorData = await response.json();
 
140
  }
141
  }
142
  } catch (error) {
143
+ posthog.capture("Improve prompt", {"type": "failed", "error": error});
144
  console.error("Error improving prompt:", error)
145
  setImproveError(error instanceof Error ? error.message : "Failed to improve prompt")
146
  } finally {