├── .eslintrc.json ├── types ├── global.d.ts ├── article.ts ├── chat.ts └── supabase.ts ├── postcss.config.js ├── app ├── template.tsx ├── loading.tsx ├── login │ ├── loading.tsx │ └── error.tsx ├── (auth) │ ├── loading.tsx │ └── error.tsx ├── components │ ├── DemoWarningBanner.tsx │ ├── ConsoleBanner.tsx │ ├── Footer.tsx │ ├── ProtectedRoute.tsx │ ├── ActivityContext.tsx │ ├── ArticleList.tsx │ ├── Toast.tsx │ ├── Header.tsx │ ├── ColorThemeSelector.tsx │ ├── AuthProvider.tsx │ ├── UnifiedDiffViewer.tsx │ └── MarkdownRenderer.tsx ├── not-found.tsx ├── providers.tsx ├── layout.tsx ├── debug-wrapper.tsx ├── global-error.tsx ├── api │ ├── monitoring │ │ └── metrics │ │ │ └── route.ts │ ├── auth │ │ ├── login │ │ │ └── route.ts │ │ └── register │ │ │ └── route.ts │ ├── save-article │ │ └── route.ts │ ├── workers │ │ └── stats │ │ │ └── route.ts │ ├── claude │ │ └── route.ts │ └── health │ │ └── route.ts ├── globals.css └── error.tsx ├── Screenshot 2025-07-11 at 17.36.34.png ├── lib ├── utils.ts ├── supabase.ts ├── init.ts ├── claude-types.ts ├── consoleBanner.ts ├── api-error-handler.ts ├── claude-prompts.ts ├── markdown-formatter.ts ├── error-boundary.tsx ├── diff-generator.ts ├── claude-integration.md ├── rate-limiter.ts ├── claude-client.ts ├── services │ └── articles.ts ├── claude-rate-limiter.ts ├── claude-service.ts └── error-handler.ts ├── stores ├── index.ts └── chatStore.ts ├── scripts ├── clear-auth.js ├── create-initial-article.js ├── check-webhook-config.js ├── setup-database.js ├── debug-registration.js └── setup-supabase-db.sh ├── instrumentation.ts ├── supabase ├── migrations │ ├── 000_create_articles_table.sql │ ├── 001_create_users_table.sql │ ├── 002_update_articles_table.sql │ ├── 006_fix_workers_columns.sql │ ├── 003_create_workers_table.sql │ ├── 005_update_articles_shared_access.sql │ ├── 004_update_workers_table_activity_tracking.sql │ └── 005_improve_workers_session_tracking.sql └── setup-database.md ├── .cursor ├── mcp.json └── rules │ ├── cursor_rules.mdc │ └── self_improve.mdc ├── tsconfig.json ├── hooks ├── index.ts ├── useClaude.ts ├── useLocalStorage.ts └── useWebSocket.ts ├── next.config.js ├── .gitignore ├── .env.production ├── tailwind.config.js ├── components ├── OfflineIndicator.tsx ├── LoadingButton.tsx ├── LoadingStates.tsx ├── LoadingSkeleton.tsx ├── ErrorFallback.tsx └── ErrorBoundary.tsx ├── .env.example ├── vercel.json ├── docs ├── TEST_OVERFLOW_FIX.md ├── DATABASE_SETUP.md ├── SETUP_SUMMARY.md ├── WEBHOOK_CONFIGURATION.md └── SUPABASE_SETUP_GUIDE.md ├── package.json └── middleware.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } -------------------------------------------------------------------------------- /types/global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | var mockUsers: any[] | undefined; 3 | } 4 | 5 | export {}; -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } -------------------------------------------------------------------------------- /app/template.tsx: -------------------------------------------------------------------------------- 1 | export default function Template({ children }: { children: React.ReactNode }) { 2 | return children; 3 | } -------------------------------------------------------------------------------- /Screenshot 2025-07-11 at 17.36.34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zie619/n8n_chat_bot_webhook_supabase/HEAD/Screenshot 2025-07-11 at 17.36.34.png -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } -------------------------------------------------------------------------------- /stores/index.ts: -------------------------------------------------------------------------------- 1 | // Export all stores from a single entry point 2 | export { useChatStore } from './chatStore' 3 | 4 | // You can add more stores here as the application grows 5 | // export { useUserStore } from './userStore' 6 | // export { useUIStore } from './uiStore' -------------------------------------------------------------------------------- /scripts/clear-auth.js: -------------------------------------------------------------------------------- 1 | // Clear authentication data from localStorage 2 | // Run this in the browser console if needed 3 | 4 | localStorage.removeItem('auth_token'); 5 | localStorage.removeItem('user_id'); 6 | localStorage.removeItem('user_email'); 7 | console.log('Auth data cleared'); -------------------------------------------------------------------------------- /instrumentation.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Next.js instrumentation file for server-side initialization 3 | * This runs once when the server starts 4 | */ 5 | 6 | export async function register() { 7 | if (process.env.NEXT_RUNTIME === 'nodejs') { 8 | // Dynamic import to avoid bundling server code in client 9 | const { initializeApp } = await import('./lib/init'); 10 | initializeApp(); 11 | } 12 | } -------------------------------------------------------------------------------- /app/loading.tsx: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return ( 3 |
4 |
5 |
6 |

Loading...

7 |
8 |
9 | ); 10 | } -------------------------------------------------------------------------------- /app/login/loading.tsx: -------------------------------------------------------------------------------- 1 | export default function LoginLoading() { 2 | return ( 3 |
4 |
5 |
6 |

Authenticating...

7 |
8 |
9 | ); 10 | } -------------------------------------------------------------------------------- /app/(auth)/loading.tsx: -------------------------------------------------------------------------------- 1 | export default function AuthLoading() { 2 | return ( 3 |
4 |
5 |
6 |

Loading authentication...

7 |
8 |
9 | ); 10 | } -------------------------------------------------------------------------------- /app/components/DemoWarningBanner.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | export default function DemoWarningBanner() { 4 | return ( 5 |
6 |

7 | ⚠️ DEMO MODE - This is a view-only demo. Most features are disabled. ⚠️ 8 |

9 |
10 | ); 11 | } -------------------------------------------------------------------------------- /types/article.ts: -------------------------------------------------------------------------------- 1 | export interface Article { 2 | id: string 3 | title: string 4 | content: string 5 | status: 'draft' | 'final' 6 | updated_at: string 7 | } 8 | 9 | export type ArticleUpdate = Partial> 10 | 11 | export interface ArticleResponse { 12 | data: Article | null 13 | error: Error | null 14 | } 15 | 16 | export interface ArticlesResponse { 17 | data: Article[] | null 18 | error: Error | null 19 | } -------------------------------------------------------------------------------- /lib/supabase.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from '@supabase/supabase-js' 2 | import type { Database } from '@/types/supabase' 3 | 4 | const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL! 5 | const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! 6 | 7 | if (!supabaseUrl || !supabaseAnonKey) { 8 | throw new Error('Missing Supabase environment variables') 9 | } 10 | 11 | export const supabase = createClient(supabaseUrl, supabaseAnonKey) -------------------------------------------------------------------------------- /supabase/migrations/000_create_articles_table.sql: -------------------------------------------------------------------------------- 1 | -- Create articles table 2 | CREATE TABLE IF NOT EXISTS articles ( 3 | id UUID DEFAULT gen_random_uuid() PRIMARY KEY, 4 | title TEXT NOT NULL, 5 | content TEXT, 6 | status TEXT DEFAULT 'draft', 7 | updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()) 8 | ); 9 | 10 | -- Add check constraint for status 11 | ALTER TABLE articles 12 | ADD CONSTRAINT valid_status CHECK (status IN ('draft', 'published', 'archived', 'final')); -------------------------------------------------------------------------------- /types/chat.ts: -------------------------------------------------------------------------------- 1 | export interface ChatMessage { 2 | id: string 3 | role: 'user' | 'assistant' | 'system' 4 | content: string 5 | timestamp: Date 6 | articleContext?: string 7 | } 8 | 9 | export interface ChatSession { 10 | id: string 11 | messages: ChatMessage[] 12 | articleId?: string 13 | createdAt: Date 14 | updatedAt: Date 15 | } 16 | 17 | export interface ChatResponse { 18 | content: string 19 | model?: string 20 | usage?: { 21 | input_tokens: number 22 | output_tokens: number 23 | } 24 | stop_reason?: string 25 | } 26 | 27 | export interface ChatError { 28 | message: string 29 | code?: string 30 | details?: any 31 | } -------------------------------------------------------------------------------- /.cursor/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "task-master-ai": { 4 | "command": "npx", 5 | "args": ["-y", "--package=task-master-ai", "task-master-ai"], 6 | "env": { 7 | "ANTHROPIC_API_KEY": "ANTHROPIC_API_KEY_HERE", 8 | "PERPLEXITY_API_KEY": "PERPLEXITY_API_KEY_HERE", 9 | "OPENAI_API_KEY": "OPENAI_API_KEY_HERE", 10 | "GOOGLE_API_KEY": "GOOGLE_API_KEY_HERE", 11 | "XAI_API_KEY": "XAI_API_KEY_HERE", 12 | "OPENROUTER_API_KEY": "OPENROUTER_API_KEY_HERE", 13 | "MISTRAL_API_KEY": "MISTRAL_API_KEY_HERE", 14 | "AZURE_OPENAI_API_KEY": "AZURE_OPENAI_API_KEY_HERE", 15 | "OLLAMA_API_KEY": "OLLAMA_API_KEY_HERE" 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/components/ConsoleBanner.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEffect } from 'react'; 4 | import { showConsoleBanner } from '@/lib/consoleBanner'; 5 | 6 | export default function ConsoleBanner() { 7 | useEffect(() => { 8 | // Show the banner when the app loads 9 | showConsoleBanner(); 10 | 11 | // Also show on route changes 12 | const handleRouteChange = () => { 13 | showConsoleBanner(); 14 | }; 15 | 16 | window.addEventListener('popstate', handleRouteChange); 17 | 18 | return () => { 19 | window.removeEventListener('popstate', handleRouteChange); 20 | }; 21 | }, []); 22 | 23 | // This component doesn't render anything 24 | return null; 25 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } -------------------------------------------------------------------------------- /types/supabase.ts: -------------------------------------------------------------------------------- 1 | export interface Database { 2 | public: { 3 | Tables: { 4 | articles: { 5 | Row: { 6 | id: string 7 | title: string 8 | content: string 9 | status: 'draft' | 'final' 10 | updated_at: string 11 | } 12 | Insert: { 13 | id?: string 14 | title: string 15 | content: string 16 | status?: 'draft' | 'final' 17 | updated_at?: string 18 | } 19 | Update: { 20 | id?: string 21 | title?: string 22 | content?: string 23 | status?: 'draft' | 'final' 24 | updated_at?: string 25 | } 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Footer() { 4 | return ( 5 | 19 | ); 20 | } -------------------------------------------------------------------------------- /hooks/index.ts: -------------------------------------------------------------------------------- 1 | // Export all hooks from a single entry point 2 | export { useArticles, useArticle } from './useArticles' 3 | export { useChat, useChatSessions } from './useChat' 4 | export { useArticleActions } from './useArticleActions' 5 | export { useWebSocket, useRealtimeArticles } from './useWebSocket' 6 | export { useLocalStorage, useChatSessionStorage, useUserPreferences } from './useLocalStorage' 7 | export { 8 | useKeyboardShortcuts, 9 | useArticleEditorShortcuts, 10 | useChatShortcuts, 11 | useGlobalShortcuts 12 | } from './useKeyboardShortcuts' 13 | 14 | // Re-export types that might be useful 15 | export type { ChatMessage, ChatSession, ChatResponse } from '@/types/chat' 16 | export type { Article, ArticleResponse } from '@/types/article' -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | experimental: { 6 | instrumentationHook: true, 7 | }, 8 | // Optimize production builds 9 | compiler: { 10 | removeConsole: process.env.NODE_ENV === 'production', 11 | }, 12 | // Enable compression 13 | compress: true, 14 | // Optimize images 15 | images: { 16 | formats: ['image/avif', 'image/webp'], 17 | }, 18 | // Reduce initial JS payload 19 | modularizeImports: { 20 | '@radix-ui': { 21 | transform: '@radix-ui/{{member}}', 22 | }, 23 | 'lucide-react': { 24 | transform: 'lucide-react/dist/esm/icons/{{member}}', 25 | }, 26 | }, 27 | } 28 | 29 | module.exports = nextConfig -------------------------------------------------------------------------------- /app/components/ProtectedRoute.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEffect } from 'react'; 4 | import { useRouter } from 'next/navigation'; 5 | import { useAuth } from './AuthProvider'; 6 | 7 | export default function ProtectedRoute({ children }: { children: React.ReactNode }) { 8 | const { user, isLoading } = useAuth(); 9 | const router = useRouter(); 10 | 11 | useEffect(() => { 12 | if (!isLoading && !user) { 13 | router.push('/login'); 14 | } 15 | }, [user, isLoading, router]); 16 | 17 | if (isLoading) { 18 | return ( 19 |
20 |
Loading...
21 |
22 | ); 23 | } 24 | 25 | if (!user) { 26 | return null; 27 | } 28 | 29 | return <>{children}; 30 | } -------------------------------------------------------------------------------- /app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | 3 | export default function NotFound() { 4 | return ( 5 |
6 |
7 |

404

8 |

9 | Page Not Found 10 |

11 |

12 | The page you're looking for doesn't exist. 13 |

14 | 18 | Go Home 19 | 20 |
21 |
22 | ); 23 | } -------------------------------------------------------------------------------- /app/login/error.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | export default function LoginError({ 4 | error, 5 | reset, 6 | }: { 7 | error: Error & { digest?: string }; 8 | reset: () => void; 9 | }) { 10 | return ( 11 |
12 |
13 |

14 | Login Error 15 |

16 |

17 | {error.message || 'Failed to authenticate'} 18 |

19 | 25 |
26 |
27 | ); 28 | } -------------------------------------------------------------------------------- /supabase/migrations/001_create_users_table.sql: -------------------------------------------------------------------------------- 1 | -- Create users table for authentication 2 | CREATE TABLE IF NOT EXISTS users ( 3 | id UUID DEFAULT gen_random_uuid() PRIMARY KEY, 4 | email TEXT UNIQUE NOT NULL, 5 | password_hash TEXT NOT NULL, 6 | created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc', NOW()), 7 | last_login TIMESTAMP WITH TIME ZONE 8 | ); 9 | 10 | -- Add RLS (Row Level Security) 11 | ALTER TABLE users ENABLE ROW LEVEL SECURITY; 12 | 13 | -- Create policy for users to read their own data 14 | CREATE POLICY "Users can view own data" ON users 15 | FOR SELECT USING (auth.uid() = id); 16 | 17 | -- Create policy for inserting new users (registration) 18 | CREATE POLICY "Anyone can register" ON users 19 | FOR INSERT WITH CHECK (true); 20 | 21 | -- Create index on email for faster lookups 22 | CREATE INDEX idx_users_email ON users(email); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | # IDEs 40 | .idea 41 | .vscode 42 | *.swp 43 | *.swo 44 | 45 | # OS 46 | Thumbs.db 47 | 48 | # Logs 49 | logs 50 | *.log 51 | dev-debug.log 52 | # Dependency directories 53 | node_modules/ 54 | # Environment variables 55 | # Editor directories and files 56 | *.suo 57 | *.ntvs* 58 | *.njsproj 59 | *.sln 60 | *.sw? 61 | # OS specific 62 | 63 | # Task files 64 | tasks.json 65 | tasks/ 66 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # Production Environment Variables 2 | # Copy this file to .env.production.local and fill in your production values 3 | 4 | # Supabase Configuration 5 | NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co 6 | NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here 7 | SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here 8 | 9 | # Authentication 10 | JWT_SECRET=your-secure-jwt-secret-min-32-chars 11 | API_KEY_SECRET=your-secure-admin-password 12 | 13 | # Claude AI 14 | ANTHROPIC_API_KEY=sk-ant-api03-your-key-here 15 | 16 | # Application URLs 17 | NEXT_PUBLIC_APP_URL=https://your-domain.com 18 | NEXT_PUBLIC_WEBHOOK_URL=https://your-webhook.com/webhook/xfunnel 19 | 20 | # Optional: Upstash Redis for Rate Limiting 21 | # UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io 22 | # UPSTASH_REDIS_REST_TOKEN=your-redis-token 23 | 24 | # Optional: Monitoring 25 | # SENTRY_DSN=https://your-sentry-dsn 26 | # VERCEL_ANALYTICS_ID=your-analytics-id 27 | 28 | # Build Configuration 29 | NODE_ENV=production 30 | NEXT_TELEMETRY_DISABLED=1 -------------------------------------------------------------------------------- /app/(auth)/error.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEffect } from 'react'; 4 | 5 | export default function AuthError({ 6 | error, 7 | reset, 8 | }: { 9 | error: Error & { digest?: string }; 10 | reset: () => void; 11 | }) { 12 | useEffect(() => { 13 | console.error('Auth error:', error); 14 | }, [error]); 15 | 16 | return ( 17 |
18 |
19 |

20 | Authentication Error 21 |

22 |

23 | {error.message || 'An error occurred during authentication'} 24 |

25 | 31 |
32 |
33 | ); 34 | } -------------------------------------------------------------------------------- /app/components/ActivityContext.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { createContext, useContext } from 'react'; 4 | 5 | interface ActivityContextType { 6 | incrementAiRequests: () => void; 7 | incrementManualEdits: () => void; 8 | } 9 | 10 | export const ActivityContext = createContext(undefined); 11 | 12 | export function useActivityContext() { 13 | const context = useContext(ActivityContext); 14 | if (!context) { 15 | throw new Error('useActivityContext must be used within an ActivityProvider'); 16 | } 17 | return context; 18 | } 19 | 20 | interface ActivityProviderProps { 21 | children: React.ReactNode; 22 | incrementAiRequests: () => void; 23 | incrementManualEdits: () => void; 24 | } 25 | 26 | export function ActivityProvider({ children, incrementAiRequests, incrementManualEdits }: ActivityProviderProps) { 27 | return ( 28 | 29 | {children} 30 | 31 | ); 32 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | ], 8 | darkMode: 'class', 9 | theme: { 10 | extend: { 11 | colors: { 12 | accent: { 13 | 50: 'var(--accent-50)', 14 | 100: 'var(--accent-100)', 15 | 200: 'var(--accent-200)', 16 | 300: 'var(--accent-300)', 17 | 400: 'var(--accent-400)', 18 | 500: 'var(--accent-500)', 19 | 600: 'var(--accent-600)', 20 | 700: 'var(--accent-700)', 21 | 800: 'var(--accent-800)', 22 | 900: 'var(--accent-900)', 23 | primary: 'var(--accent-primary)', 24 | 'primary-hover': 'var(--accent-primary-hover)', 25 | 'primary-active': 'var(--accent-primary-active)', 26 | light: 'var(--accent-light)', 27 | 'light-hover': 'var(--accent-light-hover)', 28 | dark: 'var(--accent-dark)', 29 | } 30 | }, 31 | }, 32 | }, 33 | plugins: [], 34 | } -------------------------------------------------------------------------------- /app/providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { AuthProvider } from './components/AuthProvider'; 4 | import { ThemeProvider } from './components/ThemeProvider'; 5 | import { DebugErrorBoundary } from './debug-wrapper'; 6 | import { ErrorBoundary } from '@/components/ErrorBoundary'; 7 | import { ErrorDebugPanel } from '@/components/ErrorDebugPanel'; 8 | import OfflineIndicator from '@/components/OfflineIndicator'; 9 | import { useEffect } from 'react'; 10 | import { initializeApp } from '@/lib/init'; 11 | 12 | export function Providers({ children }: { children: React.ReactNode }) { 13 | useEffect(() => { 14 | // Initialize app on client side 15 | if (typeof window !== 'undefined') { 16 | initializeApp(); 17 | } 18 | }, []); 19 | 20 | return ( 21 | 22 | 23 | 24 | 25 | {children} 26 | 27 | {process.env.NODE_ENV === 'development' && } 28 | 29 | 30 | 31 | 32 | ); 33 | } -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { Inter } from 'next/font/google' 3 | import './globals.css' 4 | 5 | const inter = Inter({ subsets: ['latin'] }) 6 | 7 | export const metadata: Metadata = { 8 | title: 'xFunnel - Article Editor with Claude AI', 9 | description: 'Edit and manage articles with AI-powered assistance from Claude', 10 | keywords: 'article editor, AI assistant, Claude, content management', 11 | } 12 | 13 | import { Providers } from './providers' 14 | import ConsoleBanner from './components/ConsoleBanner' 15 | import Footer from './components/Footer' 16 | import DemoWarningBanner from './components/DemoWarningBanner' 17 | 18 | export default function RootLayout({ 19 | children, 20 | }: { 21 | children: React.ReactNode 22 | }) { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 |
30 | {children} 31 |
32 |