lumely-next 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.
- package/README.md +60 -0
- package/dist/index.d.mts +88 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# lumely-next
|
|
2
|
+
|
|
3
|
+
AI-powered error handling for Next.js applications. Automatically catches errors, explains them to users in plain language, and provides developers with AI-generated fix suggestions.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install lumely-next
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
// pages/_app.tsx or app/layout.tsx
|
|
15
|
+
import { LumelyProvider } from 'lumely-next';
|
|
16
|
+
|
|
17
|
+
export default function App({ Component, pageProps }) {
|
|
18
|
+
return (
|
|
19
|
+
<LumelyProvider
|
|
20
|
+
apiKey={process.env.NEXT_PUBLIC_LUMELY_API_KEY || 'dev-key'}
|
|
21
|
+
environment={process.env.NODE_ENV === 'production' ? 'production' : 'development'}
|
|
22
|
+
>
|
|
23
|
+
<Component {...pageProps} />
|
|
24
|
+
</LumelyProvider>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
That's it. Lumely will automatically catch errors and display a user-friendly overlay.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **AI-Powered Explanations** - Errors are explained in plain language
|
|
34
|
+
- **User Feedback** - Collect context about what users were doing
|
|
35
|
+
- **Modern Glassmorphism UI** - Beautiful overlay with Poppins font
|
|
36
|
+
- **Fast Response** - User message appears in ~2s, detailed analysis runs in background
|
|
37
|
+
- **Secure** - API keys stay on your server
|
|
38
|
+
- **Next.js Optimized** - Uses next/font for optimal font loading
|
|
39
|
+
|
|
40
|
+
## Props
|
|
41
|
+
|
|
42
|
+
| Prop | Type | Required | Description |
|
|
43
|
+
|------|------|----------|-------------|
|
|
44
|
+
| `apiKey` | `string` | Yes | Your Lumely API key |
|
|
45
|
+
| `environment` | `'development' \| 'production'` | No | Environment name |
|
|
46
|
+
| `userId` | `string` | No | Current user's ID |
|
|
47
|
+
| `sessionId` | `string` | No | Session ID (auto-generated if not provided) |
|
|
48
|
+
| `onError` | `(report) => void` | No | Callback when error is caught |
|
|
49
|
+
|
|
50
|
+
## API Routes
|
|
51
|
+
|
|
52
|
+
This package expects API routes at `/api/lumely/`. Set up the API by copying the route handlers from the Lumely documentation.
|
|
53
|
+
|
|
54
|
+
## Documentation
|
|
55
|
+
|
|
56
|
+
Visit [lumely.vercel.app](https://lumely.vercel.app) for full documentation.
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React, { Component, ErrorInfo } from 'react';
|
|
3
|
+
|
|
4
|
+
interface LumelyConfig {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
environment?: 'development' | 'production';
|
|
7
|
+
userId?: string;
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
onError?: (error: LumelyErrorReport) => void;
|
|
10
|
+
}
|
|
11
|
+
interface LumelyErrorContext {
|
|
12
|
+
url: string;
|
|
13
|
+
userAgent: string;
|
|
14
|
+
userId?: string;
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
userFeedback?: string;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
interface LumelyErrorReport {
|
|
20
|
+
errorMessage: string;
|
|
21
|
+
errorStack?: string;
|
|
22
|
+
componentStack?: string;
|
|
23
|
+
context: LumelyErrorContext;
|
|
24
|
+
}
|
|
25
|
+
interface LumelyAIResponse {
|
|
26
|
+
userMessage: string;
|
|
27
|
+
summary: string;
|
|
28
|
+
suggestedFix: string;
|
|
29
|
+
}
|
|
30
|
+
interface LumelyAPIResponse {
|
|
31
|
+
success: boolean;
|
|
32
|
+
ai: LumelyAIResponse;
|
|
33
|
+
errorId: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface LumelyProviderProps {
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
apiKey: string;
|
|
39
|
+
environment?: 'development' | 'production';
|
|
40
|
+
userId?: string;
|
|
41
|
+
sessionId?: string;
|
|
42
|
+
onError?: LumelyConfig['onError'];
|
|
43
|
+
}
|
|
44
|
+
declare const LumelyProvider: ({ children, apiKey, environment, userId, sessionId, onError, }: LumelyProviderProps) => react_jsx_runtime.JSX.Element;
|
|
45
|
+
|
|
46
|
+
interface Props {
|
|
47
|
+
children: React.ReactNode;
|
|
48
|
+
config: LumelyConfig;
|
|
49
|
+
}
|
|
50
|
+
interface State {
|
|
51
|
+
hasError: boolean;
|
|
52
|
+
error: Error | null;
|
|
53
|
+
errorInfo: ErrorInfo | null;
|
|
54
|
+
aiResponse: LumelyAIResponse | null;
|
|
55
|
+
isLoading: boolean;
|
|
56
|
+
errorId: string | null;
|
|
57
|
+
feedbackSubmitted: boolean;
|
|
58
|
+
}
|
|
59
|
+
declare class LumelyErrorBoundary extends Component<Props, State> {
|
|
60
|
+
private isReporting;
|
|
61
|
+
constructor(props: Props);
|
|
62
|
+
static getDerivedStateFromError(error: Error): Partial<State>;
|
|
63
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
64
|
+
private reportError;
|
|
65
|
+
private handleSubmitFeedback;
|
|
66
|
+
private handleRetry;
|
|
67
|
+
private handleDismiss;
|
|
68
|
+
private handleGoBack;
|
|
69
|
+
render(): string | number | boolean | Iterable<React.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface LumelyErrorOverlayProps {
|
|
73
|
+
aiResponse?: LumelyAIResponse;
|
|
74
|
+
isLoading: boolean;
|
|
75
|
+
feedbackSubmitted?: boolean;
|
|
76
|
+
onSubmitFeedback: (feedback: string) => void;
|
|
77
|
+
onRetry: () => void;
|
|
78
|
+
onDismiss: () => void;
|
|
79
|
+
onGoBack: () => void;
|
|
80
|
+
}
|
|
81
|
+
declare const LumelyErrorOverlay: ({ aiResponse, isLoading, feedbackSubmitted, onSubmitFeedback, onRetry, onDismiss, onGoBack, }: LumelyErrorOverlayProps) => react_jsx_runtime.JSX.Element;
|
|
82
|
+
|
|
83
|
+
interface LumelyContextValue extends LumelyConfig {
|
|
84
|
+
sessionId: string;
|
|
85
|
+
}
|
|
86
|
+
declare const useLumelyContext: () => LumelyContextValue;
|
|
87
|
+
|
|
88
|
+
export { type LumelyAIResponse, type LumelyAPIResponse, type LumelyConfig, LumelyErrorBoundary, type LumelyErrorContext, LumelyErrorOverlay, type LumelyErrorReport, LumelyProvider, useLumelyContext };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React, { Component, ErrorInfo } from 'react';
|
|
3
|
+
|
|
4
|
+
interface LumelyConfig {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
environment?: 'development' | 'production';
|
|
7
|
+
userId?: string;
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
onError?: (error: LumelyErrorReport) => void;
|
|
10
|
+
}
|
|
11
|
+
interface LumelyErrorContext {
|
|
12
|
+
url: string;
|
|
13
|
+
userAgent: string;
|
|
14
|
+
userId?: string;
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
userFeedback?: string;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
interface LumelyErrorReport {
|
|
20
|
+
errorMessage: string;
|
|
21
|
+
errorStack?: string;
|
|
22
|
+
componentStack?: string;
|
|
23
|
+
context: LumelyErrorContext;
|
|
24
|
+
}
|
|
25
|
+
interface LumelyAIResponse {
|
|
26
|
+
userMessage: string;
|
|
27
|
+
summary: string;
|
|
28
|
+
suggestedFix: string;
|
|
29
|
+
}
|
|
30
|
+
interface LumelyAPIResponse {
|
|
31
|
+
success: boolean;
|
|
32
|
+
ai: LumelyAIResponse;
|
|
33
|
+
errorId: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface LumelyProviderProps {
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
apiKey: string;
|
|
39
|
+
environment?: 'development' | 'production';
|
|
40
|
+
userId?: string;
|
|
41
|
+
sessionId?: string;
|
|
42
|
+
onError?: LumelyConfig['onError'];
|
|
43
|
+
}
|
|
44
|
+
declare const LumelyProvider: ({ children, apiKey, environment, userId, sessionId, onError, }: LumelyProviderProps) => react_jsx_runtime.JSX.Element;
|
|
45
|
+
|
|
46
|
+
interface Props {
|
|
47
|
+
children: React.ReactNode;
|
|
48
|
+
config: LumelyConfig;
|
|
49
|
+
}
|
|
50
|
+
interface State {
|
|
51
|
+
hasError: boolean;
|
|
52
|
+
error: Error | null;
|
|
53
|
+
errorInfo: ErrorInfo | null;
|
|
54
|
+
aiResponse: LumelyAIResponse | null;
|
|
55
|
+
isLoading: boolean;
|
|
56
|
+
errorId: string | null;
|
|
57
|
+
feedbackSubmitted: boolean;
|
|
58
|
+
}
|
|
59
|
+
declare class LumelyErrorBoundary extends Component<Props, State> {
|
|
60
|
+
private isReporting;
|
|
61
|
+
constructor(props: Props);
|
|
62
|
+
static getDerivedStateFromError(error: Error): Partial<State>;
|
|
63
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
64
|
+
private reportError;
|
|
65
|
+
private handleSubmitFeedback;
|
|
66
|
+
private handleRetry;
|
|
67
|
+
private handleDismiss;
|
|
68
|
+
private handleGoBack;
|
|
69
|
+
render(): string | number | boolean | Iterable<React.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface LumelyErrorOverlayProps {
|
|
73
|
+
aiResponse?: LumelyAIResponse;
|
|
74
|
+
isLoading: boolean;
|
|
75
|
+
feedbackSubmitted?: boolean;
|
|
76
|
+
onSubmitFeedback: (feedback: string) => void;
|
|
77
|
+
onRetry: () => void;
|
|
78
|
+
onDismiss: () => void;
|
|
79
|
+
onGoBack: () => void;
|
|
80
|
+
}
|
|
81
|
+
declare const LumelyErrorOverlay: ({ aiResponse, isLoading, feedbackSubmitted, onSubmitFeedback, onRetry, onDismiss, onGoBack, }: LumelyErrorOverlayProps) => react_jsx_runtime.JSX.Element;
|
|
82
|
+
|
|
83
|
+
interface LumelyContextValue extends LumelyConfig {
|
|
84
|
+
sessionId: string;
|
|
85
|
+
}
|
|
86
|
+
declare const useLumelyContext: () => LumelyContextValue;
|
|
87
|
+
|
|
88
|
+
export { type LumelyAIResponse, type LumelyAPIResponse, type LumelyConfig, LumelyErrorBoundary, type LumelyErrorContext, LumelyErrorOverlay, type LumelyErrorReport, LumelyProvider, useLumelyContext };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime'),google=require('next/font/google');var f=react.createContext(null),k=()=>{if(typeof window=="undefined")return "server";let o=sessionStorage.getItem("lumely_session_id");if(o)return o;let i=`sess_${Date.now()}_${Math.random().toString(36).substring(2,9)}`;return sessionStorage.setItem("lumely_session_id",i),i},I=()=>{let o=react.useContext(f);if(!o)throw new Error("useLumelyContext must be used within a LumelyProvider");return o},y=({children:o,config:i})=>{let t=react.useMemo(()=>({...i,sessionId:i.sessionId||k(),environment:i.environment||"production"}),[i]);return jsxRuntime.jsx(f.Provider,{value:t,children:o})};var C=google.Poppins({subsets:["latin"],weight:["400","500","600","700"],variable:"--font-poppins"}),p=({aiResponse:o,isLoading:i,feedbackSubmitted:t=false,onSubmitFeedback:r,onRetry:s,onDismiss:a,onGoBack:n})=>{let[d,g]=react.useState(""),[c,x]=react.useState(false),v=async m=>{m.preventDefault(),d.trim()&&!c&&(x(true),r(d));};return jsxRuntime.jsx("div",{className:`${C.className} fixed inset-0 z-[9999] flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm`,children:jsxRuntime.jsxs("div",{className:"relative w-full max-w-md bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl overflow-hidden",children:[jsxRuntime.jsx("button",{onClick:a,className:"absolute top-3 right-3 p-1.5 text-white/60 hover:text-white hover:bg-white/10 rounded-lg transition-colors",children:"\u2715"}),jsxRuntime.jsxs("div",{className:"p-6",children:[jsxRuntime.jsxs("div",{className:"flex items-center gap-3 mb-4",children:[jsxRuntime.jsx("div",{className:"w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center",children:jsxRuntime.jsx("span",{className:"text-white font-bold text-lg",children:"L"})}),jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("h3",{className:"text-white font-semibold text-sm",children:"Something went wrong"}),jsxRuntime.jsx("p",{className:"text-white/50 text-xs",children:"We're looking into it"})]})]}),i?jsxRuntime.jsxs("div",{className:"flex items-center gap-2 text-white/70 text-sm py-4",children:[jsxRuntime.jsx("div",{className:"w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin"}),jsxRuntime.jsx("span",{children:"Analyzing the issue..."})]}):o?jsxRuntime.jsx("div",{className:"bg-white/5 border border-white/10 rounded-xl p-4 mb-4",children:jsxRuntime.jsx("p",{className:"text-white/90 text-sm leading-relaxed",children:o.userMessage})}):jsxRuntime.jsx("div",{className:"bg-white/5 border border-white/10 rounded-xl p-4 mb-4",children:jsxRuntime.jsx("p",{className:"text-white/90 text-sm leading-relaxed",children:"We encountered an unexpected issue. Our team has been notified."})}),!t&&!c?jsxRuntime.jsxs("form",{onSubmit:v,className:"mb-4",children:[jsxRuntime.jsx("label",{className:"block text-white/60 text-xs mb-2",children:"What were you trying to do?"}),jsxRuntime.jsxs("div",{className:"relative",children:[jsxRuntime.jsx("input",{type:"text",value:d,onChange:m=>g(m.target.value),placeholder:"e.g., I was trying to save my changes...",className:"w-full bg-white/5 border border-white/10 rounded-xl py-3 px-4 pr-12 text-white text-sm placeholder:text-white/30 focus:outline-none focus:border-primary-500/50 transition-colors"}),jsxRuntime.jsx("button",{type:"submit",disabled:!d.trim(),className:"absolute right-2 top-1/2 -translate-y-1/2 p-2 text-white/40 hover:text-primary-400 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",children:"\u27A4"})]})]}):jsxRuntime.jsx("div",{className:"bg-green-500/10 border border-green-500/20 rounded-xl p-3 mb-4",children:jsxRuntime.jsx("p",{className:"text-green-400 text-xs text-center",children:"Thank you! Your feedback helps us improve."})}),jsxRuntime.jsxs("div",{className:"flex items-center gap-2",children:[jsxRuntime.jsx("button",{onClick:n,className:"flex-1 flex items-center justify-center gap-2 py-2.5 px-4 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-white/80 text-sm font-medium transition-colors",children:"\u2190 Go Back"}),jsxRuntime.jsx("button",{onClick:s,className:"flex-1 flex items-center justify-center gap-2 py-2.5 px-4 bg-primary-600 hover:bg-primary-500 rounded-xl text-white text-sm font-medium transition-colors",children:"\u21BB Try Again"})]})]})]})})};var u=class extends react.Component{constructor(t){super(t);this.isReporting=false;this.handleSubmitFeedback=async t=>{let{errorId:r}=this.state,{config:s}=this.props;if(!r||r==="no-db"||r==="db-error"){this.setState({feedbackSubmitted:true});return}try{await fetch("/api/lumely/update-feedback",{method:"POST",headers:{"Content-Type":"application/json","X-Lumely-Key":s.apiKey},body:JSON.stringify({errorId:r,feedback:t})}),this.setState({feedbackSubmitted:!0});}catch(a){console.error("Lumely: Failed to submit feedback",a),this.setState({feedbackSubmitted:true});}};this.handleRetry=()=>{this.isReporting=false,this.setState({hasError:false,error:null,errorInfo:null,aiResponse:null,isLoading:false,errorId:null,feedbackSubmitted:false});};this.handleDismiss=()=>{this.isReporting=false,this.setState({hasError:false,error:null,errorInfo:null,aiResponse:null,isLoading:false,errorId:null,feedbackSubmitted:false});};this.handleGoBack=()=>{typeof window!="undefined"&&window.history.back();};this.state={hasError:false,error:null,errorInfo:null,aiResponse:null,isLoading:false,errorId:null,feedbackSubmitted:false};}static getDerivedStateFromError(t){return {hasError:true,error:t}}componentDidCatch(t,r){this.isReporting||(this.isReporting=true,this.setState({errorInfo:r,isLoading:true}),this.reportError(t,r));}async reportError(t,r){let{config:s}=this.props,a={errorMessage:t.message,errorStack:t.stack,componentStack:r.componentStack||void 0,context:{url:typeof window!="undefined"?window.location.href:"",userAgent:typeof navigator!="undefined"?navigator.userAgent:"",userId:s.userId,sessionId:s.sessionId,timestamp:new Date().toISOString()}};try{let n=await fetch("/api/lumely/report-error",{method:"POST",headers:{"Content-Type":"application/json","X-Lumely-Key":s.apiKey},body:JSON.stringify(a)});if(n.ok){let d=await n.json();this.setState({aiResponse:d.ai,errorId:d.errorId,isLoading:!1});}else this.setState({isLoading:!1});}catch(n){console.error("Lumely: Failed to report error",n),this.setState({isLoading:false});}s.onError&&s.onError(a);}render(){let{hasError:t,aiResponse:r,isLoading:s,feedbackSubmitted:a}=this.state,{children:n}=this.props;return t?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[n,jsxRuntime.jsx(p,{aiResponse:r||void 0,isLoading:s,feedbackSubmitted:a,onSubmitFeedback:this.handleSubmitFeedback,onRetry:this.handleRetry,onDismiss:this.handleDismiss,onGoBack:this.handleGoBack})]}):n}};var D=({children:o,apiKey:i,environment:t="production",userId:r,sessionId:s,onError:a})=>{let n={apiKey:i,environment:t,userId:r,sessionId:s,onError:a};return jsxRuntime.jsx(y,{config:n,children:jsxRuntime.jsx(u,{config:n,children:o})})};exports.LumelyErrorBoundary=u;exports.LumelyErrorOverlay=p;exports.LumelyProvider=D;exports.useLumelyContext=I;//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../LumelyContext.tsx","../LumelyErrorOverlay.tsx","../LumelyErrorBoundary.tsx","../LumelyProvider.tsx"],"names":["LumelyContext","createContext","generateSessionId","stored","newId","useLumelyContext","context","useContext","LumelyContextProvider","children","config","value","useMemo","jsx","poppins","Poppins","LumelyErrorOverlay","aiResponse","isLoading","feedbackSubmitted","onSubmitFeedback","onRetry","onDismiss","onGoBack","feedback","setFeedback","useState","isSubmitting","setIsSubmitting","handleSubmit","e","jsxs","LumelyErrorBoundary","Component","props","errorId","err","error","errorInfo","report","response","data","hasError","Fragment","LumelyProvider","apiKey","environment","userId","sessionId","onError"],"mappings":"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","file":"index.js","sourcesContent":["'use client';\r\n\r\nimport React, { createContext, useContext, useMemo } from 'react';\r\nimport type { LumelyConfig } from './types';\r\n\r\ninterface LumelyContextValue extends LumelyConfig {\r\n sessionId: string;\r\n}\r\n\r\nconst LumelyContext = createContext<LumelyContextValue | null>(null);\r\n\r\n// Generate a unique session ID\r\nconst generateSessionId = () => {\r\n if (typeof window === 'undefined') return 'server';\r\n const stored = sessionStorage.getItem('lumely_session_id');\r\n if (stored) return stored;\r\n const newId = `sess_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\r\n sessionStorage.setItem('lumely_session_id', newId);\r\n return newId;\r\n};\r\n\r\nexport const useLumelyContext = () => {\r\n const context = useContext(LumelyContext);\r\n if (!context) {\r\n throw new Error('useLumelyContext must be used within a LumelyProvider');\r\n }\r\n return context;\r\n};\r\n\r\ninterface LumelyContextProviderProps {\r\n children: React.ReactNode;\r\n config: LumelyConfig;\r\n}\r\n\r\nexport const LumelyContextProvider = ({ children, config }: LumelyContextProviderProps) => {\r\n const value = useMemo(() => ({\r\n ...config,\r\n sessionId: config.sessionId || generateSessionId(),\r\n environment: config.environment || 'production',\r\n }), [config]);\r\n\r\n return (\r\n <LumelyContext.Provider value={value}>\r\n {children}\r\n </LumelyContext.Provider>\r\n );\r\n};\r\n","'use client';\r\n\r\nimport React, { useState } from 'react';\r\nimport { Poppins } from 'next/font/google';\r\nimport type { LumelyAIResponse } from './types';\r\n\r\nconst poppins = Poppins({\r\n subsets: ['latin'],\r\n weight: ['400', '500', '600', '700'],\r\n variable: '--font-poppins'\r\n});\r\n\r\ninterface LumelyErrorOverlayProps {\r\n aiResponse?: LumelyAIResponse;\r\n isLoading: boolean;\r\n feedbackSubmitted?: boolean;\r\n onSubmitFeedback: (feedback: string) => void;\r\n onRetry: () => void;\r\n onDismiss: () => void;\r\n onGoBack: () => void;\r\n}\r\n\r\nexport const LumelyErrorOverlay = ({\r\n aiResponse,\r\n isLoading,\r\n feedbackSubmitted = false,\r\n onSubmitFeedback,\r\n onRetry,\r\n onDismiss,\r\n onGoBack,\r\n}: LumelyErrorOverlayProps) => {\r\n const [feedback, setFeedback] = useState('');\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (feedback.trim() && !isSubmitting) {\r\n setIsSubmitting(true);\r\n onSubmitFeedback(feedback);\r\n }\r\n };\r\n\r\n return (\r\n <div className={`${poppins.className} fixed inset-0 z-[9999] flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm`}>\r\n {/* Glassmorphism Card */}\r\n <div className=\"relative w-full max-w-md bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl overflow-hidden\">\r\n {/* Close Button */}\r\n <button\r\n onClick={onDismiss}\r\n className=\"absolute top-3 right-3 p-1.5 text-white/60 hover:text-white hover:bg-white/10 rounded-lg transition-colors\"\r\n >\r\n ✕\r\n </button>\r\n\r\n {/* Content */}\r\n <div className=\"p-6\">\r\n {/* Header */}\r\n <div className=\"flex items-center gap-3 mb-4\">\r\n <div className=\"w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center\">\r\n <span className=\"text-white font-bold text-lg\">L</span>\r\n </div>\r\n <div>\r\n <h3 className=\"text-white font-semibold text-sm\">Something went wrong</h3>\r\n <p className=\"text-white/50 text-xs\">We're looking into it</p>\r\n </div>\r\n </div>\r\n\r\n {/* AI Message */}\r\n {isLoading ? (\r\n <div className=\"flex items-center gap-2 text-white/70 text-sm py-4\">\r\n <div className=\"w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin\" />\r\n <span>Analyzing the issue...</span>\r\n </div>\r\n ) : aiResponse ? (\r\n <div className=\"bg-white/5 border border-white/10 rounded-xl p-4 mb-4\">\r\n <p className=\"text-white/90 text-sm leading-relaxed\">\r\n {aiResponse.userMessage}\r\n </p>\r\n </div>\r\n ) : (\r\n <div className=\"bg-white/5 border border-white/10 rounded-xl p-4 mb-4\">\r\n <p className=\"text-white/90 text-sm leading-relaxed\">\r\n We encountered an unexpected issue. Our team has been notified.\r\n </p>\r\n </div>\r\n )}\r\n\r\n {/* User Feedback Input */}\r\n {!feedbackSubmitted && !isSubmitting ? (\r\n <form onSubmit={handleSubmit} className=\"mb-4\">\r\n <label className=\"block text-white/60 text-xs mb-2\">\r\n What were you trying to do?\r\n </label>\r\n <div className=\"relative\">\r\n <input\r\n type=\"text\"\r\n value={feedback}\r\n onChange={(e) => setFeedback(e.target.value)}\r\n placeholder=\"e.g., I was trying to save my changes...\"\r\n className=\"w-full bg-white/5 border border-white/10 rounded-xl py-3 px-4 pr-12 text-white text-sm placeholder:text-white/30 focus:outline-none focus:border-primary-500/50 transition-colors\"\r\n />\r\n <button\r\n type=\"submit\"\r\n disabled={!feedback.trim()}\r\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-2 text-white/40 hover:text-primary-400 disabled:opacity-30 disabled:cursor-not-allowed transition-colors\"\r\n >\r\n ➤\r\n </button>\r\n </div>\r\n </form>\r\n ) : (\r\n <div className=\"bg-green-500/10 border border-green-500/20 rounded-xl p-3 mb-4\">\r\n <p className=\"text-green-400 text-xs text-center\">\r\n Thank you! Your feedback helps us improve.\r\n </p>\r\n </div>\r\n )}\r\n\r\n {/* Action Buttons */}\r\n <div className=\"flex items-center gap-2\">\r\n <button\r\n onClick={onGoBack}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-2.5 px-4 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-white/80 text-sm font-medium transition-colors\"\r\n >\r\n ← Go Back\r\n </button>\r\n <button\r\n onClick={onRetry}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-2.5 px-4 bg-primary-600 hover:bg-primary-500 rounded-xl text-white text-sm font-medium transition-colors\"\r\n >\r\n ↻ Try Again\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n};\r\n","'use client';\r\n\r\nimport React, { Component, ErrorInfo } from 'react';\r\nimport { LumelyErrorOverlay } from './LumelyErrorOverlay';\r\nimport type { LumelyConfig, LumelyErrorReport, LumelyAIResponse, LumelyAPIResponse } from './types';\r\n\r\ninterface Props {\r\n children: React.ReactNode;\r\n config: LumelyConfig;\r\n}\r\n\r\ninterface State {\r\n hasError: boolean;\r\n error: Error | null;\r\n errorInfo: ErrorInfo | null;\r\n aiResponse: LumelyAIResponse | null;\r\n isLoading: boolean;\r\n errorId: string | null;\r\n feedbackSubmitted: boolean;\r\n}\r\n\r\nexport class LumelyErrorBoundary extends Component<Props, State> {\r\n private isReporting = false; // Prevent duplicate reports\r\n\r\n constructor(props: Props) {\r\n super(props);\r\n this.state = {\r\n hasError: false,\r\n error: null,\r\n errorInfo: null,\r\n aiResponse: null,\r\n isLoading: false,\r\n errorId: null,\r\n feedbackSubmitted: false,\r\n };\r\n }\r\n\r\n static getDerivedStateFromError(error: Error): Partial<State> {\r\n return { hasError: true, error };\r\n }\r\n\r\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\r\n // Prevent duplicate reports\r\n if (this.isReporting) return;\r\n\r\n this.isReporting = true;\r\n this.setState({ errorInfo, isLoading: true });\r\n this.reportError(error, errorInfo);\r\n }\r\n\r\n private async reportError(error: Error, errorInfo: ErrorInfo) {\r\n const { config } = this.props;\r\n\r\n const report: LumelyErrorReport = {\r\n errorMessage: error.message,\r\n errorStack: error.stack,\r\n componentStack: errorInfo.componentStack || undefined,\r\n context: {\r\n url: typeof window !== 'undefined' ? window.location.href : '',\r\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : '',\r\n userId: config.userId,\r\n sessionId: config.sessionId,\r\n timestamp: new Date().toISOString(),\r\n },\r\n };\r\n\r\n try {\r\n const response = await fetch('/api/lumely/report-error', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Lumely-Key': config.apiKey,\r\n },\r\n body: JSON.stringify(report),\r\n });\r\n\r\n if (response.ok) {\r\n const data: LumelyAPIResponse = await response.json();\r\n this.setState({\r\n aiResponse: data.ai,\r\n errorId: data.errorId,\r\n isLoading: false,\r\n });\r\n } else {\r\n this.setState({ isLoading: false });\r\n }\r\n } catch (err) {\r\n console.error('Lumely: Failed to report error', err);\r\n this.setState({ isLoading: false });\r\n }\r\n\r\n // Call optional callback\r\n if (config.onError) {\r\n config.onError(report);\r\n }\r\n }\r\n\r\n private handleSubmitFeedback = async (feedback: string) => {\r\n const { errorId } = this.state;\r\n const { config } = this.props;\r\n\r\n if (!errorId || errorId === 'no-db' || errorId === 'db-error') {\r\n // Can't update if no valid error ID\r\n this.setState({ feedbackSubmitted: true });\r\n return;\r\n }\r\n\r\n try {\r\n // Update existing error record with feedback\r\n await fetch('/api/lumely/update-feedback', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Lumely-Key': config.apiKey,\r\n },\r\n body: JSON.stringify({ errorId, feedback }),\r\n });\r\n\r\n this.setState({ feedbackSubmitted: true });\r\n } catch (err) {\r\n console.error('Lumely: Failed to submit feedback', err);\r\n this.setState({ feedbackSubmitted: true });\r\n }\r\n };\r\n\r\n private handleRetry = () => {\r\n this.isReporting = false;\r\n this.setState({\r\n hasError: false,\r\n error: null,\r\n errorInfo: null,\r\n aiResponse: null,\r\n isLoading: false,\r\n errorId: null,\r\n feedbackSubmitted: false,\r\n });\r\n };\r\n\r\n private handleDismiss = () => {\r\n this.isReporting = false;\r\n this.setState({\r\n hasError: false,\r\n error: null,\r\n errorInfo: null,\r\n aiResponse: null,\r\n isLoading: false,\r\n errorId: null,\r\n feedbackSubmitted: false,\r\n });\r\n };\r\n\r\n private handleGoBack = () => {\r\n if (typeof window !== 'undefined') {\r\n window.history.back();\r\n }\r\n };\r\n\r\n render() {\r\n const { hasError, aiResponse, isLoading, feedbackSubmitted } = this.state;\r\n const { children } = this.props;\r\n\r\n if (hasError) {\r\n return (\r\n <>\r\n {children}\r\n <LumelyErrorOverlay\r\n aiResponse={aiResponse || undefined}\r\n isLoading={isLoading}\r\n feedbackSubmitted={feedbackSubmitted}\r\n onSubmitFeedback={this.handleSubmitFeedback}\r\n onRetry={this.handleRetry}\r\n onDismiss={this.handleDismiss}\r\n onGoBack={this.handleGoBack}\r\n />\r\n </>\r\n );\r\n }\r\n\r\n return children;\r\n }\r\n}\r\n","'use client';\r\n\r\nimport React from 'react';\r\nimport { LumelyContextProvider } from './LumelyContext';\r\nimport { LumelyErrorBoundary } from './LumelyErrorBoundary';\r\nimport type { LumelyConfig } from './types';\r\n\r\ninterface LumelyProviderProps {\r\n children: React.ReactNode;\r\n apiKey: string;\r\n environment?: 'development' | 'production';\r\n userId?: string;\r\n sessionId?: string;\r\n onError?: LumelyConfig['onError'];\r\n}\r\n\r\nexport const LumelyProvider = ({\r\n children,\r\n apiKey,\r\n environment = 'production',\r\n userId,\r\n sessionId,\r\n onError,\r\n}: LumelyProviderProps) => {\r\n const config: LumelyConfig = {\r\n apiKey,\r\n environment,\r\n userId,\r\n sessionId,\r\n onError,\r\n };\r\n\r\n return (\r\n <LumelyContextProvider config={config}>\r\n <LumelyErrorBoundary config={config}>\r\n {children}\r\n </LumelyErrorBoundary>\r\n </LumelyContextProvider>\r\n );\r\n};\r\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {createContext,useContext,useState,Component,useMemo}from'react';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {Poppins}from'next/font/google';var f=createContext(null),k=()=>{if(typeof window=="undefined")return "server";let o=sessionStorage.getItem("lumely_session_id");if(o)return o;let i=`sess_${Date.now()}_${Math.random().toString(36).substring(2,9)}`;return sessionStorage.setItem("lumely_session_id",i),i},I=()=>{let o=useContext(f);if(!o)throw new Error("useLumelyContext must be used within a LumelyProvider");return o},y=({children:o,config:i})=>{let t=useMemo(()=>({...i,sessionId:i.sessionId||k(),environment:i.environment||"production"}),[i]);return jsx(f.Provider,{value:t,children:o})};var C=Poppins({subsets:["latin"],weight:["400","500","600","700"],variable:"--font-poppins"}),p=({aiResponse:o,isLoading:i,feedbackSubmitted:t=false,onSubmitFeedback:r,onRetry:s,onDismiss:a,onGoBack:n})=>{let[d,g]=useState(""),[c,x]=useState(false),v=async m=>{m.preventDefault(),d.trim()&&!c&&(x(true),r(d));};return jsx("div",{className:`${C.className} fixed inset-0 z-[9999] flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm`,children:jsxs("div",{className:"relative w-full max-w-md bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl overflow-hidden",children:[jsx("button",{onClick:a,className:"absolute top-3 right-3 p-1.5 text-white/60 hover:text-white hover:bg-white/10 rounded-lg transition-colors",children:"\u2715"}),jsxs("div",{className:"p-6",children:[jsxs("div",{className:"flex items-center gap-3 mb-4",children:[jsx("div",{className:"w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center",children:jsx("span",{className:"text-white font-bold text-lg",children:"L"})}),jsxs("div",{children:[jsx("h3",{className:"text-white font-semibold text-sm",children:"Something went wrong"}),jsx("p",{className:"text-white/50 text-xs",children:"We're looking into it"})]})]}),i?jsxs("div",{className:"flex items-center gap-2 text-white/70 text-sm py-4",children:[jsx("div",{className:"w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin"}),jsx("span",{children:"Analyzing the issue..."})]}):o?jsx("div",{className:"bg-white/5 border border-white/10 rounded-xl p-4 mb-4",children:jsx("p",{className:"text-white/90 text-sm leading-relaxed",children:o.userMessage})}):jsx("div",{className:"bg-white/5 border border-white/10 rounded-xl p-4 mb-4",children:jsx("p",{className:"text-white/90 text-sm leading-relaxed",children:"We encountered an unexpected issue. Our team has been notified."})}),!t&&!c?jsxs("form",{onSubmit:v,className:"mb-4",children:[jsx("label",{className:"block text-white/60 text-xs mb-2",children:"What were you trying to do?"}),jsxs("div",{className:"relative",children:[jsx("input",{type:"text",value:d,onChange:m=>g(m.target.value),placeholder:"e.g., I was trying to save my changes...",className:"w-full bg-white/5 border border-white/10 rounded-xl py-3 px-4 pr-12 text-white text-sm placeholder:text-white/30 focus:outline-none focus:border-primary-500/50 transition-colors"}),jsx("button",{type:"submit",disabled:!d.trim(),className:"absolute right-2 top-1/2 -translate-y-1/2 p-2 text-white/40 hover:text-primary-400 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",children:"\u27A4"})]})]}):jsx("div",{className:"bg-green-500/10 border border-green-500/20 rounded-xl p-3 mb-4",children:jsx("p",{className:"text-green-400 text-xs text-center",children:"Thank you! Your feedback helps us improve."})}),jsxs("div",{className:"flex items-center gap-2",children:[jsx("button",{onClick:n,className:"flex-1 flex items-center justify-center gap-2 py-2.5 px-4 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-white/80 text-sm font-medium transition-colors",children:"\u2190 Go Back"}),jsx("button",{onClick:s,className:"flex-1 flex items-center justify-center gap-2 py-2.5 px-4 bg-primary-600 hover:bg-primary-500 rounded-xl text-white text-sm font-medium transition-colors",children:"\u21BB Try Again"})]})]})]})})};var u=class extends Component{constructor(t){super(t);this.isReporting=false;this.handleSubmitFeedback=async t=>{let{errorId:r}=this.state,{config:s}=this.props;if(!r||r==="no-db"||r==="db-error"){this.setState({feedbackSubmitted:true});return}try{await fetch("/api/lumely/update-feedback",{method:"POST",headers:{"Content-Type":"application/json","X-Lumely-Key":s.apiKey},body:JSON.stringify({errorId:r,feedback:t})}),this.setState({feedbackSubmitted:!0});}catch(a){console.error("Lumely: Failed to submit feedback",a),this.setState({feedbackSubmitted:true});}};this.handleRetry=()=>{this.isReporting=false,this.setState({hasError:false,error:null,errorInfo:null,aiResponse:null,isLoading:false,errorId:null,feedbackSubmitted:false});};this.handleDismiss=()=>{this.isReporting=false,this.setState({hasError:false,error:null,errorInfo:null,aiResponse:null,isLoading:false,errorId:null,feedbackSubmitted:false});};this.handleGoBack=()=>{typeof window!="undefined"&&window.history.back();};this.state={hasError:false,error:null,errorInfo:null,aiResponse:null,isLoading:false,errorId:null,feedbackSubmitted:false};}static getDerivedStateFromError(t){return {hasError:true,error:t}}componentDidCatch(t,r){this.isReporting||(this.isReporting=true,this.setState({errorInfo:r,isLoading:true}),this.reportError(t,r));}async reportError(t,r){let{config:s}=this.props,a={errorMessage:t.message,errorStack:t.stack,componentStack:r.componentStack||void 0,context:{url:typeof window!="undefined"?window.location.href:"",userAgent:typeof navigator!="undefined"?navigator.userAgent:"",userId:s.userId,sessionId:s.sessionId,timestamp:new Date().toISOString()}};try{let n=await fetch("/api/lumely/report-error",{method:"POST",headers:{"Content-Type":"application/json","X-Lumely-Key":s.apiKey},body:JSON.stringify(a)});if(n.ok){let d=await n.json();this.setState({aiResponse:d.ai,errorId:d.errorId,isLoading:!1});}else this.setState({isLoading:!1});}catch(n){console.error("Lumely: Failed to report error",n),this.setState({isLoading:false});}s.onError&&s.onError(a);}render(){let{hasError:t,aiResponse:r,isLoading:s,feedbackSubmitted:a}=this.state,{children:n}=this.props;return t?jsxs(Fragment,{children:[n,jsx(p,{aiResponse:r||void 0,isLoading:s,feedbackSubmitted:a,onSubmitFeedback:this.handleSubmitFeedback,onRetry:this.handleRetry,onDismiss:this.handleDismiss,onGoBack:this.handleGoBack})]}):n}};var D=({children:o,apiKey:i,environment:t="production",userId:r,sessionId:s,onError:a})=>{let n={apiKey:i,environment:t,userId:r,sessionId:s,onError:a};return jsx(y,{config:n,children:jsx(u,{config:n,children:o})})};export{u as LumelyErrorBoundary,p as LumelyErrorOverlay,D as LumelyProvider,I as useLumelyContext};//# sourceMappingURL=index.mjs.map
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../LumelyContext.tsx","../LumelyErrorOverlay.tsx","../LumelyErrorBoundary.tsx","../LumelyProvider.tsx"],"names":["LumelyContext","createContext","generateSessionId","stored","newId","useLumelyContext","context","useContext","LumelyContextProvider","children","config","value","useMemo","jsx","poppins","Poppins","LumelyErrorOverlay","aiResponse","isLoading","feedbackSubmitted","onSubmitFeedback","onRetry","onDismiss","onGoBack","feedback","setFeedback","useState","isSubmitting","setIsSubmitting","handleSubmit","e","jsxs","LumelyErrorBoundary","Component","props","errorId","err","error","errorInfo","report","response","data","hasError","Fragment","LumelyProvider","apiKey","environment","userId","sessionId","onError"],"mappings":"iKASA,IAAMA,EAAgBC,aAAAA,CAAyC,IAAI,CAAA,CAG7DC,CAAAA,CAAoB,IAAM,CAC5B,GAAI,OAAO,MAAA,EAAW,WAAA,CAAa,OAAO,QAAA,CAC1C,IAAMC,EAAS,cAAA,CAAe,OAAA,CAAQ,mBAAmB,CAAA,CACzD,GAAIA,EAAQ,OAAOA,CAAAA,CACnB,IAAMC,CAAAA,CAAQ,CAAA,KAAA,EAAQ,IAAA,CAAK,KAAK,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAA,CAAU,CAAA,CAAG,CAAC,CAAC,CAAA,CAAA,CAC9E,sBAAe,OAAA,CAAQ,mBAAA,CAAqBA,CAAK,CAAA,CAC1CA,CACX,EAEaC,CAAAA,CAAmB,IAAM,CAClC,IAAMC,CAAAA,CAAUC,UAAAA,CAAWP,CAAa,CAAA,CACxC,GAAI,CAACM,CAAAA,CACD,MAAM,IAAI,MAAM,uDAAuD,CAAA,CAE3E,OAAOA,CACX,CAAA,CAOaE,CAAAA,CAAwB,CAAC,CAAE,QAAA,CAAAC,CAAAA,CAAU,MAAA,CAAAC,CAAO,CAAA,GAAkC,CACvF,IAAMC,CAAAA,CAAQC,OAAAA,CAAQ,KAAO,CACzB,GAAGF,EACH,SAAA,CAAWA,CAAAA,CAAO,SAAA,EAAaR,CAAAA,EAAkB,CACjD,WAAA,CAAaQ,EAAO,WAAA,EAAe,YACvC,CAAA,CAAA,CAAI,CAACA,CAAM,CAAC,EAEZ,OACIG,GAAAA,CAACb,EAAc,QAAA,CAAd,CAAuB,MAAOW,CAAAA,CAC1B,QAAA,CAAAF,CAAAA,CACL,CAER,ECxCA,IAAMK,CAAAA,CAAUC,OAAAA,CAAQ,CACpB,OAAA,CAAS,CAAC,OAAO,CAAA,CACjB,MAAA,CAAQ,CAAC,KAAA,CAAO,KAAA,CAAO,MAAO,KAAK,CAAA,CACnC,QAAA,CAAU,gBACd,CAAC,CAAA,CAYYC,EAAqB,CAAC,CAC/B,UAAA,CAAAC,CAAAA,CACA,SAAA,CAAAC,CAAAA,CACA,kBAAAC,CAAAA,CAAoB,KAAA,CACpB,iBAAAC,CAAAA,CACA,OAAA,CAAAC,EACA,SAAA,CAAAC,CAAAA,CACA,QAAA,CAAAC,CACJ,CAAA,GAA+B,CAC3B,GAAM,CAACC,CAAAA,CAAUC,CAAW,CAAA,CAAIC,QAAAA,CAAS,EAAE,EACrC,CAACC,CAAAA,CAAcC,CAAe,CAAA,CAAIF,QAAAA,CAAS,KAAK,EAEhDG,CAAAA,CAAe,MAAOC,CAAAA,EAAuB,CAC/CA,CAAAA,CAAE,cAAA,GACEN,CAAAA,CAAS,IAAA,EAAK,EAAK,CAACG,CAAAA,GACpBC,CAAAA,CAAgB,IAAI,CAAA,CACpBR,CAAAA,CAAiBI,CAAQ,CAAA,EAEjC,CAAA,CAEA,OACIX,IAAC,KAAA,CAAA,CAAI,SAAA,CAAW,CAAA,EAAGC,CAAAA,CAAQ,SAAS,CAAA,yFAAA,CAAA,CAEhC,SAAAiB,IAAAA,CAAC,KAAA,CAAA,CAAI,UAAU,0GAAA,CAEX,QAAA,CAAA,CAAAlB,IAAC,QAAA,CAAA,CACG,OAAA,CAASS,CAAAA,CACT,SAAA,CAAU,4GAAA,CACb,QAAA,CAAA,QAAA,CAED,EAGAS,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,KAAA,CAEX,QAAA,CAAA,CAAAA,IAAAA,CAAC,OAAI,SAAA,CAAU,8BAAA,CACX,QAAA,CAAA,CAAAlB,GAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,0GACX,QAAA,CAAAA,GAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,8BAAA,CAA+B,QAAA,CAAA,GAAA,CAAC,EACpD,CAAA,CACAkB,IAAAA,CAAC,KAAA,CAAA,CACG,QAAA,CAAA,CAAAlB,GAAAA,CAAC,IAAA,CAAA,CAAG,UAAU,kCAAA,CAAmC,QAAA,CAAA,sBAAA,CAAoB,CAAA,CACrEA,GAAAA,CAAC,GAAA,CAAA,CAAE,SAAA,CAAU,wBAAwB,QAAA,CAAA,uBAAA,CAAqB,CAAA,CAAA,CAC9D,CAAA,CAAA,CACJ,CAAA,CAGCK,CAAAA,CACGa,IAAAA,CAAC,OAAI,SAAA,CAAU,oDAAA,CACX,UAAAlB,GAAAA,CAAC,KAAA,CAAA,CAAI,UAAU,2EAAA,CAA4E,CAAA,CAC3FA,GAAAA,CAAC,MAAA,CAAA,CAAK,QAAA,CAAA,wBAAA,CAAsB,CAAA,CAAA,CAChC,EACAI,CAAAA,CACAJ,GAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,uDAAA,CACX,QAAA,CAAAA,IAAC,GAAA,CAAA,CAAE,SAAA,CAAU,uCAAA,CACR,QAAA,CAAAI,CAAAA,CAAW,WAAA,CAChB,EACJ,CAAA,CAEAJ,GAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,uDAAA,CACX,QAAA,CAAAA,IAAC,GAAA,CAAA,CAAE,SAAA,CAAU,uCAAA,CAAwC,QAAA,CAAA,iEAAA,CAErD,CAAA,CACJ,CAAA,CAIH,CAACM,CAAAA,EAAqB,CAACQ,CAAAA,CACpBI,IAAAA,CAAC,MAAA,CAAA,CAAK,QAAA,CAAUF,EAAc,SAAA,CAAU,MAAA,CACpC,QAAA,CAAA,CAAAhB,GAAAA,CAAC,OAAA,CAAA,CAAM,SAAA,CAAU,mCAAmC,QAAA,CAAA,6BAAA,CAEpD,CAAA,CACAkB,KAAC,KAAA,CAAA,CAAI,SAAA,CAAU,WACX,QAAA,CAAA,CAAAlB,GAAAA,CAAC,OAAA,CAAA,CACG,IAAA,CAAK,MAAA,CACL,KAAA,CAAOW,EACP,QAAA,CAAWM,CAAAA,EAAML,CAAAA,CAAYK,CAAAA,CAAE,MAAA,CAAO,KAAK,EAC3C,WAAA,CAAY,0CAAA,CACZ,SAAA,CAAU,mLAAA,CACd,CAAA,CACAjB,GAAAA,CAAC,UACG,IAAA,CAAK,QAAA,CACL,QAAA,CAAU,CAACW,CAAAA,CAAS,IAAA,GACpB,SAAA,CAAU,sJAAA,CACb,QAAA,CAAA,QAAA,CAED,CAAA,CAAA,CACJ,CAAA,CAAA,CACJ,CAAA,CAEAX,IAAC,KAAA,CAAA,CAAI,SAAA,CAAU,gEAAA,CACX,QAAA,CAAAA,GAAAA,CAAC,GAAA,CAAA,CAAE,UAAU,oCAAA,CAAqC,QAAA,CAAA,4CAAA,CAElD,CAAA,CACJ,CAAA,CAIJkB,IAAAA,CAAC,KAAA,CAAA,CAAI,UAAU,yBAAA,CACX,QAAA,CAAA,CAAAlB,IAAC,QAAA,CAAA,CACG,OAAA,CAASU,EACT,SAAA,CAAU,8KAAA,CACb,QAAA,CAAA,gBAAA,CAED,CAAA,CACAV,GAAAA,CAAC,QAAA,CAAA,CACG,QAASQ,CAAAA,CACT,SAAA,CAAU,2JAAA,CACb,QAAA,CAAA,kBAAA,CAED,CAAA,CAAA,CACJ,CAAA,CAAA,CACJ,GACJ,CAAA,CACJ,CAER,ECpHO,IAAMW,CAAAA,CAAN,cAAkCC,SAAwB,CAG7D,WAAA,CAAYC,CAAAA,CAAc,CACtB,KAAA,CAAMA,CAAK,EAHf,IAAA,CAAQ,WAAA,CAAc,KAAA,CA2EtB,IAAA,CAAQ,oBAAA,CAAuB,MAAOV,GAAqB,CACvD,GAAM,CAAE,OAAA,CAAAW,CAAQ,CAAA,CAAI,KAAK,KAAA,CACnB,CAAE,OAAAzB,CAAO,CAAA,CAAI,KAAK,KAAA,CAExB,GAAI,CAACyB,CAAAA,EAAWA,CAAAA,GAAY,OAAA,EAAWA,IAAY,UAAA,CAAY,CAE3D,IAAA,CAAK,QAAA,CAAS,CAAE,iBAAA,CAAmB,IAAK,CAAC,CAAA,CACzC,MACJ,CAEA,GAAI,CAEA,MAAM,KAAA,CAAM,6BAAA,CAA+B,CACvC,MAAA,CAAQ,MAAA,CACR,OAAA,CAAS,CACL,cAAA,CAAgB,kBAAA,CAChB,cAAA,CAAgBzB,CAAAA,CAAO,MAC3B,CAAA,CACA,KAAM,IAAA,CAAK,SAAA,CAAU,CAAE,OAAA,CAAAyB,CAAAA,CAAS,QAAA,CAAAX,CAAS,CAAC,CAC9C,CAAC,CAAA,CAED,IAAA,CAAK,QAAA,CAAS,CAAE,iBAAA,CAAmB,CAAA,CAAK,CAAC,EAC7C,CAAA,MAASY,EAAK,CACV,OAAA,CAAQ,KAAA,CAAM,mCAAA,CAAqCA,CAAG,CAAA,CACtD,KAAK,QAAA,CAAS,CAAE,iBAAA,CAAmB,IAAK,CAAC,EAC7C,CACJ,CAAA,CAEA,IAAA,CAAQ,WAAA,CAAc,IAAM,CACxB,IAAA,CAAK,YAAc,KAAA,CACnB,IAAA,CAAK,QAAA,CAAS,CACV,QAAA,CAAU,KAAA,CACV,MAAO,IAAA,CACP,SAAA,CAAW,IAAA,CACX,UAAA,CAAY,IAAA,CACZ,SAAA,CAAW,MACX,OAAA,CAAS,IAAA,CACT,iBAAA,CAAmB,KACvB,CAAC,EACL,EAEA,IAAA,CAAQ,aAAA,CAAgB,IAAM,CAC1B,IAAA,CAAK,WAAA,CAAc,MACnB,IAAA,CAAK,QAAA,CAAS,CACV,QAAA,CAAU,KAAA,CACV,MAAO,IAAA,CACP,SAAA,CAAW,IAAA,CACX,UAAA,CAAY,IAAA,CACZ,SAAA,CAAW,MACX,OAAA,CAAS,IAAA,CACT,iBAAA,CAAmB,KACvB,CAAC,EACL,EAEA,IAAA,CAAQ,YAAA,CAAe,IAAM,CACrB,OAAO,MAAA,EAAW,aAClB,MAAA,CAAO,OAAA,CAAQ,IAAA,GAEvB,CAAA,CAjII,IAAA,CAAK,MAAQ,CACT,QAAA,CAAU,KAAA,CACV,KAAA,CAAO,IAAA,CACP,SAAA,CAAW,KACX,UAAA,CAAY,IAAA,CACZ,SAAA,CAAW,KAAA,CACX,OAAA,CAAS,IAAA,CACT,kBAAmB,KACvB,EACJ,CAEA,OAAO,wBAAA,CAAyBC,CAAAA,CAA8B,CAC1D,OAAO,CAAE,SAAU,IAAA,CAAM,KAAA,CAAAA,CAAM,CACnC,CAEA,iBAAA,CAAkBA,CAAAA,CAAcC,CAAAA,CAAsB,CAE9C,KAAK,WAAA,GAET,IAAA,CAAK,WAAA,CAAc,IAAA,CACnB,IAAA,CAAK,QAAA,CAAS,CAAE,SAAA,CAAAA,CAAAA,CAAW,SAAA,CAAW,IAAK,CAAC,CAAA,CAC5C,KAAK,WAAA,CAAYD,CAAAA,CAAOC,CAAS,CAAA,EACrC,CAEA,MAAc,YAAYD,CAAAA,CAAcC,CAAAA,CAAsB,CAC1D,GAAM,CAAE,MAAA,CAAA5B,CAAO,CAAA,CAAI,IAAA,CAAK,KAAA,CAElB6B,CAAAA,CAA4B,CAC9B,YAAA,CAAcF,EAAM,OAAA,CACpB,UAAA,CAAYA,CAAAA,CAAM,KAAA,CAClB,cAAA,CAAgBC,CAAAA,CAAU,gBAAkB,MAAA,CAC5C,OAAA,CAAS,CACL,GAAA,CAAK,OAAO,QAAW,WAAA,CAAc,MAAA,CAAO,QAAA,CAAS,IAAA,CAAO,EAAA,CAC5D,SAAA,CAAW,OAAO,SAAA,EAAc,WAAA,CAAc,SAAA,CAAU,SAAA,CAAY,EAAA,CACpE,MAAA,CAAQ5B,EAAO,MAAA,CACf,SAAA,CAAWA,CAAAA,CAAO,SAAA,CAClB,SAAA,CAAW,IAAI,MAAK,CAAE,WAAA,EAC1B,CACJ,CAAA,CAEA,GAAI,CACA,IAAM8B,CAAAA,CAAW,MAAM,KAAA,CAAM,0BAAA,CAA4B,CACrD,OAAQ,MAAA,CACR,OAAA,CAAS,CACL,cAAA,CAAgB,kBAAA,CAChB,cAAA,CAAgB9B,EAAO,MAC3B,CAAA,CACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU6B,CAAM,CAC/B,CAAC,CAAA,CAED,GAAIC,CAAAA,CAAS,EAAA,CAAI,CACb,IAAMC,CAAAA,CAA0B,MAAMD,CAAAA,CAAS,IAAA,EAAK,CACpD,KAAK,QAAA,CAAS,CACV,UAAA,CAAYC,CAAAA,CAAK,EAAA,CACjB,OAAA,CAASA,EAAK,OAAA,CACd,SAAA,CAAW,CAAA,CACf,CAAC,EACL,CAAA,KACI,KAAK,QAAA,CAAS,CAAE,SAAA,CAAW,CAAA,CAAM,CAAC,EAE1C,OAASL,CAAAA,CAAK,CACV,OAAA,CAAQ,KAAA,CAAM,gCAAA,CAAkCA,CAAG,EACnD,IAAA,CAAK,QAAA,CAAS,CAAE,SAAA,CAAW,KAAM,CAAC,EACtC,CAGI1B,CAAAA,CAAO,OAAA,EACPA,CAAAA,CAAO,OAAA,CAAQ6B,CAAM,EAE7B,CA8DA,MAAA,EAAS,CACL,GAAM,CAAE,SAAAG,CAAAA,CAAU,UAAA,CAAAzB,CAAAA,CAAY,SAAA,CAAAC,CAAAA,CAAW,iBAAA,CAAAC,CAAkB,CAAA,CAAI,IAAA,CAAK,KAAA,CAC9D,CAAE,QAAA,CAAAV,CAAS,EAAI,IAAA,CAAK,KAAA,CAE1B,OAAIiC,CAAAA,CAEIX,IAAAA,CAAAY,QAAAA,CAAA,CACK,QAAA,CAAA,CAAAlC,CAAAA,CACDI,GAAAA,CAACG,CAAAA,CAAA,CACG,UAAA,CAAYC,GAAc,MAAA,CAC1B,SAAA,CAAWC,CAAAA,CACX,iBAAA,CAAmBC,CAAAA,CACnB,gBAAA,CAAkB,KAAK,oBAAA,CACvB,OAAA,CAAS,IAAA,CAAK,WAAA,CACd,SAAA,CAAW,IAAA,CAAK,cAChB,QAAA,CAAU,IAAA,CAAK,YAAA,CACnB,CAAA,CAAA,CACJ,CAAA,CAIDV,CACX,CACJ,ECpKO,IAAMmC,EAAiB,CAAC,CAC3B,QAAA,CAAAnC,CAAAA,CACA,MAAA,CAAAoC,CAAAA,CACA,YAAAC,CAAAA,CAAc,YAAA,CACd,MAAA,CAAAC,CAAAA,CACA,SAAA,CAAAC,CAAAA,CACA,QAAAC,CACJ,CAAA,GAA2B,CACvB,IAAMvC,CAAAA,CAAuB,CACzB,OAAAmC,CAAAA,CACA,WAAA,CAAAC,CAAAA,CACA,MAAA,CAAAC,CAAAA,CACA,SAAA,CAAAC,EACA,OAAA,CAAAC,CACJ,CAAA,CAEA,OACIpC,GAAAA,CAACL,CAAAA,CAAA,CAAsB,MAAA,CAAQE,CAAAA,CAC3B,QAAA,CAAAG,GAAAA,CAACmB,CAAAA,CAAA,CAAoB,OAAQtB,CAAAA,CACxB,QAAA,CAAAD,CAAAA,CACL,CAAA,CACJ,CAER","file":"index.mjs","sourcesContent":["'use client';\r\n\r\nimport React, { createContext, useContext, useMemo } from 'react';\r\nimport type { LumelyConfig } from './types';\r\n\r\ninterface LumelyContextValue extends LumelyConfig {\r\n sessionId: string;\r\n}\r\n\r\nconst LumelyContext = createContext<LumelyContextValue | null>(null);\r\n\r\n// Generate a unique session ID\r\nconst generateSessionId = () => {\r\n if (typeof window === 'undefined') return 'server';\r\n const stored = sessionStorage.getItem('lumely_session_id');\r\n if (stored) return stored;\r\n const newId = `sess_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\r\n sessionStorage.setItem('lumely_session_id', newId);\r\n return newId;\r\n};\r\n\r\nexport const useLumelyContext = () => {\r\n const context = useContext(LumelyContext);\r\n if (!context) {\r\n throw new Error('useLumelyContext must be used within a LumelyProvider');\r\n }\r\n return context;\r\n};\r\n\r\ninterface LumelyContextProviderProps {\r\n children: React.ReactNode;\r\n config: LumelyConfig;\r\n}\r\n\r\nexport const LumelyContextProvider = ({ children, config }: LumelyContextProviderProps) => {\r\n const value = useMemo(() => ({\r\n ...config,\r\n sessionId: config.sessionId || generateSessionId(),\r\n environment: config.environment || 'production',\r\n }), [config]);\r\n\r\n return (\r\n <LumelyContext.Provider value={value}>\r\n {children}\r\n </LumelyContext.Provider>\r\n );\r\n};\r\n","'use client';\r\n\r\nimport React, { useState } from 'react';\r\nimport { Poppins } from 'next/font/google';\r\nimport type { LumelyAIResponse } from './types';\r\n\r\nconst poppins = Poppins({\r\n subsets: ['latin'],\r\n weight: ['400', '500', '600', '700'],\r\n variable: '--font-poppins'\r\n});\r\n\r\ninterface LumelyErrorOverlayProps {\r\n aiResponse?: LumelyAIResponse;\r\n isLoading: boolean;\r\n feedbackSubmitted?: boolean;\r\n onSubmitFeedback: (feedback: string) => void;\r\n onRetry: () => void;\r\n onDismiss: () => void;\r\n onGoBack: () => void;\r\n}\r\n\r\nexport const LumelyErrorOverlay = ({\r\n aiResponse,\r\n isLoading,\r\n feedbackSubmitted = false,\r\n onSubmitFeedback,\r\n onRetry,\r\n onDismiss,\r\n onGoBack,\r\n}: LumelyErrorOverlayProps) => {\r\n const [feedback, setFeedback] = useState('');\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (feedback.trim() && !isSubmitting) {\r\n setIsSubmitting(true);\r\n onSubmitFeedback(feedback);\r\n }\r\n };\r\n\r\n return (\r\n <div className={`${poppins.className} fixed inset-0 z-[9999] flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm`}>\r\n {/* Glassmorphism Card */}\r\n <div className=\"relative w-full max-w-md bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl overflow-hidden\">\r\n {/* Close Button */}\r\n <button\r\n onClick={onDismiss}\r\n className=\"absolute top-3 right-3 p-1.5 text-white/60 hover:text-white hover:bg-white/10 rounded-lg transition-colors\"\r\n >\r\n ✕\r\n </button>\r\n\r\n {/* Content */}\r\n <div className=\"p-6\">\r\n {/* Header */}\r\n <div className=\"flex items-center gap-3 mb-4\">\r\n <div className=\"w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center\">\r\n <span className=\"text-white font-bold text-lg\">L</span>\r\n </div>\r\n <div>\r\n <h3 className=\"text-white font-semibold text-sm\">Something went wrong</h3>\r\n <p className=\"text-white/50 text-xs\">We're looking into it</p>\r\n </div>\r\n </div>\r\n\r\n {/* AI Message */}\r\n {isLoading ? (\r\n <div className=\"flex items-center gap-2 text-white/70 text-sm py-4\">\r\n <div className=\"w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin\" />\r\n <span>Analyzing the issue...</span>\r\n </div>\r\n ) : aiResponse ? (\r\n <div className=\"bg-white/5 border border-white/10 rounded-xl p-4 mb-4\">\r\n <p className=\"text-white/90 text-sm leading-relaxed\">\r\n {aiResponse.userMessage}\r\n </p>\r\n </div>\r\n ) : (\r\n <div className=\"bg-white/5 border border-white/10 rounded-xl p-4 mb-4\">\r\n <p className=\"text-white/90 text-sm leading-relaxed\">\r\n We encountered an unexpected issue. Our team has been notified.\r\n </p>\r\n </div>\r\n )}\r\n\r\n {/* User Feedback Input */}\r\n {!feedbackSubmitted && !isSubmitting ? (\r\n <form onSubmit={handleSubmit} className=\"mb-4\">\r\n <label className=\"block text-white/60 text-xs mb-2\">\r\n What were you trying to do?\r\n </label>\r\n <div className=\"relative\">\r\n <input\r\n type=\"text\"\r\n value={feedback}\r\n onChange={(e) => setFeedback(e.target.value)}\r\n placeholder=\"e.g., I was trying to save my changes...\"\r\n className=\"w-full bg-white/5 border border-white/10 rounded-xl py-3 px-4 pr-12 text-white text-sm placeholder:text-white/30 focus:outline-none focus:border-primary-500/50 transition-colors\"\r\n />\r\n <button\r\n type=\"submit\"\r\n disabled={!feedback.trim()}\r\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-2 text-white/40 hover:text-primary-400 disabled:opacity-30 disabled:cursor-not-allowed transition-colors\"\r\n >\r\n ➤\r\n </button>\r\n </div>\r\n </form>\r\n ) : (\r\n <div className=\"bg-green-500/10 border border-green-500/20 rounded-xl p-3 mb-4\">\r\n <p className=\"text-green-400 text-xs text-center\">\r\n Thank you! Your feedback helps us improve.\r\n </p>\r\n </div>\r\n )}\r\n\r\n {/* Action Buttons */}\r\n <div className=\"flex items-center gap-2\">\r\n <button\r\n onClick={onGoBack}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-2.5 px-4 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-white/80 text-sm font-medium transition-colors\"\r\n >\r\n ← Go Back\r\n </button>\r\n <button\r\n onClick={onRetry}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-2.5 px-4 bg-primary-600 hover:bg-primary-500 rounded-xl text-white text-sm font-medium transition-colors\"\r\n >\r\n ↻ Try Again\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n};\r\n","'use client';\r\n\r\nimport React, { Component, ErrorInfo } from 'react';\r\nimport { LumelyErrorOverlay } from './LumelyErrorOverlay';\r\nimport type { LumelyConfig, LumelyErrorReport, LumelyAIResponse, LumelyAPIResponse } from './types';\r\n\r\ninterface Props {\r\n children: React.ReactNode;\r\n config: LumelyConfig;\r\n}\r\n\r\ninterface State {\r\n hasError: boolean;\r\n error: Error | null;\r\n errorInfo: ErrorInfo | null;\r\n aiResponse: LumelyAIResponse | null;\r\n isLoading: boolean;\r\n errorId: string | null;\r\n feedbackSubmitted: boolean;\r\n}\r\n\r\nexport class LumelyErrorBoundary extends Component<Props, State> {\r\n private isReporting = false; // Prevent duplicate reports\r\n\r\n constructor(props: Props) {\r\n super(props);\r\n this.state = {\r\n hasError: false,\r\n error: null,\r\n errorInfo: null,\r\n aiResponse: null,\r\n isLoading: false,\r\n errorId: null,\r\n feedbackSubmitted: false,\r\n };\r\n }\r\n\r\n static getDerivedStateFromError(error: Error): Partial<State> {\r\n return { hasError: true, error };\r\n }\r\n\r\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\r\n // Prevent duplicate reports\r\n if (this.isReporting) return;\r\n\r\n this.isReporting = true;\r\n this.setState({ errorInfo, isLoading: true });\r\n this.reportError(error, errorInfo);\r\n }\r\n\r\n private async reportError(error: Error, errorInfo: ErrorInfo) {\r\n const { config } = this.props;\r\n\r\n const report: LumelyErrorReport = {\r\n errorMessage: error.message,\r\n errorStack: error.stack,\r\n componentStack: errorInfo.componentStack || undefined,\r\n context: {\r\n url: typeof window !== 'undefined' ? window.location.href : '',\r\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : '',\r\n userId: config.userId,\r\n sessionId: config.sessionId,\r\n timestamp: new Date().toISOString(),\r\n },\r\n };\r\n\r\n try {\r\n const response = await fetch('/api/lumely/report-error', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Lumely-Key': config.apiKey,\r\n },\r\n body: JSON.stringify(report),\r\n });\r\n\r\n if (response.ok) {\r\n const data: LumelyAPIResponse = await response.json();\r\n this.setState({\r\n aiResponse: data.ai,\r\n errorId: data.errorId,\r\n isLoading: false,\r\n });\r\n } else {\r\n this.setState({ isLoading: false });\r\n }\r\n } catch (err) {\r\n console.error('Lumely: Failed to report error', err);\r\n this.setState({ isLoading: false });\r\n }\r\n\r\n // Call optional callback\r\n if (config.onError) {\r\n config.onError(report);\r\n }\r\n }\r\n\r\n private handleSubmitFeedback = async (feedback: string) => {\r\n const { errorId } = this.state;\r\n const { config } = this.props;\r\n\r\n if (!errorId || errorId === 'no-db' || errorId === 'db-error') {\r\n // Can't update if no valid error ID\r\n this.setState({ feedbackSubmitted: true });\r\n return;\r\n }\r\n\r\n try {\r\n // Update existing error record with feedback\r\n await fetch('/api/lumely/update-feedback', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Lumely-Key': config.apiKey,\r\n },\r\n body: JSON.stringify({ errorId, feedback }),\r\n });\r\n\r\n this.setState({ feedbackSubmitted: true });\r\n } catch (err) {\r\n console.error('Lumely: Failed to submit feedback', err);\r\n this.setState({ feedbackSubmitted: true });\r\n }\r\n };\r\n\r\n private handleRetry = () => {\r\n this.isReporting = false;\r\n this.setState({\r\n hasError: false,\r\n error: null,\r\n errorInfo: null,\r\n aiResponse: null,\r\n isLoading: false,\r\n errorId: null,\r\n feedbackSubmitted: false,\r\n });\r\n };\r\n\r\n private handleDismiss = () => {\r\n this.isReporting = false;\r\n this.setState({\r\n hasError: false,\r\n error: null,\r\n errorInfo: null,\r\n aiResponse: null,\r\n isLoading: false,\r\n errorId: null,\r\n feedbackSubmitted: false,\r\n });\r\n };\r\n\r\n private handleGoBack = () => {\r\n if (typeof window !== 'undefined') {\r\n window.history.back();\r\n }\r\n };\r\n\r\n render() {\r\n const { hasError, aiResponse, isLoading, feedbackSubmitted } = this.state;\r\n const { children } = this.props;\r\n\r\n if (hasError) {\r\n return (\r\n <>\r\n {children}\r\n <LumelyErrorOverlay\r\n aiResponse={aiResponse || undefined}\r\n isLoading={isLoading}\r\n feedbackSubmitted={feedbackSubmitted}\r\n onSubmitFeedback={this.handleSubmitFeedback}\r\n onRetry={this.handleRetry}\r\n onDismiss={this.handleDismiss}\r\n onGoBack={this.handleGoBack}\r\n />\r\n </>\r\n );\r\n }\r\n\r\n return children;\r\n }\r\n}\r\n","'use client';\r\n\r\nimport React from 'react';\r\nimport { LumelyContextProvider } from './LumelyContext';\r\nimport { LumelyErrorBoundary } from './LumelyErrorBoundary';\r\nimport type { LumelyConfig } from './types';\r\n\r\ninterface LumelyProviderProps {\r\n children: React.ReactNode;\r\n apiKey: string;\r\n environment?: 'development' | 'production';\r\n userId?: string;\r\n sessionId?: string;\r\n onError?: LumelyConfig['onError'];\r\n}\r\n\r\nexport const LumelyProvider = ({\r\n children,\r\n apiKey,\r\n environment = 'production',\r\n userId,\r\n sessionId,\r\n onError,\r\n}: LumelyProviderProps) => {\r\n const config: LumelyConfig = {\r\n apiKey,\r\n environment,\r\n userId,\r\n sessionId,\r\n onError,\r\n };\r\n\r\n return (\r\n <LumelyContextProvider config={config}>\r\n <LumelyErrorBoundary config={config}>\r\n {children}\r\n </LumelyErrorBoundary>\r\n </LumelyContextProvider>\r\n );\r\n};\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lumely-next",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI-powered error handling for Next.js applications",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"next": ">=13.0.0",
|
|
25
|
+
"react": ">=17.0.0",
|
|
26
|
+
"react-dom": ">=17.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/react": "^18.0.0",
|
|
30
|
+
"@types/react-dom": "^18.0.0",
|
|
31
|
+
"next": "^14.0.0",
|
|
32
|
+
"react": "^18.3.1",
|
|
33
|
+
"react-dom": "^18.0.0",
|
|
34
|
+
"tsup": "^8.0.0",
|
|
35
|
+
"typescript": "^5.0.0"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"lumely",
|
|
39
|
+
"next",
|
|
40
|
+
"nextjs",
|
|
41
|
+
"error-handling",
|
|
42
|
+
"error-boundary",
|
|
43
|
+
"ai"
|
|
44
|
+
],
|
|
45
|
+
"author": "Lumely",
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/lumely/lumely-next"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://lumely.vercel.app",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/lumely/lumely-next/issues"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"lucide": "^0.562.0"
|
|
57
|
+
}
|
|
58
|
+
}
|