From fb5daccc4426bb8e40396c041056e46bb4cb3aee Mon Sep 17 00:00:00 2001 From: Paritosh Kulkarni Date: Sat, 13 Dec 2025 14:21:12 -0800 Subject: [PATCH 1/3] feat: Add 3 critical UX improvements (save state, example prompts, auto-expand) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements three high-impact UX improvements identified from user journey analysis: ## 1. Save State Indicator (#1 Priority - CRITICAL) **Problem**: Users had no visibility into whether changes were saved **Solution**: Real-time visual feedback in top bar - Created SaveStateManager to track save states (saved/saving/unsaved) - Integrated into EditorEngine lifecycle - CodeManager notifies on write start/complete/error - Visual indicator shows: spinning "Saving...", checkmark "Saved", orange dot "Unsaved" - Tooltip displays time since last save **Files**: - apps/web/client/src/components/store/editor/save-state/index.ts (NEW) - apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx (NEW) - apps/web/client/src/components/store/editor/engine.ts - apps/web/client/src/components/store/editor/code/index.ts - apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx **Impact**: Builds user trust, eliminates confusion about save status --- ## 2. Example Prompts (#5 - Conversion Boost) **Problem**: Empty chat provided no guidance for new users **Solution**: Mode-specific clickable example prompts - 3 examples per mode (CREATE/EDIT/ASK/FIX) - Auto-send message on click - Examples: "Add hero section", "Change button color", "Explain component", "Fix layout" **Files**: - apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx (NEW) - apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx - apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx **Impact**: Reduces time-to-first-message, lowers cognitive load --- ## 3. Auto-Expand Last Tool Call (#4 - Transparency) **Problem**: Users manually clicked every tool to see AI actions **Solution**: Latest tool call auto-expands - Identifies last tool in message parts - Passes defaultOpen flag to CollapsibleCodeBlock - Previous tools remain collapsed - Works with all code editing tools **Files**: - apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx - apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/tool-call-display.tsx - apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx **Impact**: Improves transparency, reduces clicks --- ## Testing All changes type-checked successfully with `bun run typecheck` ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../chat-messages/example-prompts.tsx | 137 ++++++++++++++++++ .../chat-tab/chat-messages/index.tsx | 10 +- .../chat-messages/message-content/index.tsx | 13 ++ .../message-content/tool-call-display.tsx | 10 +- .../chat-tab/chat-tab-content/index.tsx | 1 + .../code-display/collapsible-code-block.tsx | 4 +- .../[id]/_components/top-bar/index.tsx | 2 + .../_components/top-bar/save-indicator.tsx | 62 ++++++++ .../src/components/store/editor/code/index.ts | 3 + .../src/components/store/editor/engine.ts | 3 + .../store/editor/save-state/index.ts | 85 +++++++++++ 11 files changed, 321 insertions(+), 9 deletions(-) create mode 100644 apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx create mode 100644 apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx create mode 100644 apps/web/client/src/components/store/editor/save-state/index.ts diff --git a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx new file mode 100644 index 0000000000..522e34155b --- /dev/null +++ b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx @@ -0,0 +1,137 @@ +'use client'; + +import { useEditorEngine } from '@/components/store/editor'; +import { ChatType } from '@onlook/models'; +import { Button } from '@onlook/ui/button'; +import { Icons } from '@onlook/ui/icons'; +import { observer } from 'mobx-react-lite'; + +interface ExamplePrompt { + icon: keyof typeof Icons; + text: string; + prompt: string; +} + +const EXAMPLE_PROMPTS: Record = { + [ChatType.CREATE]: [ + { + icon: 'MagicWand', + text: 'Add a hero section', + prompt: 'Create a modern hero section with a heading, subheading, and CTA button', + }, + { + icon: 'Component', + text: 'Build a contact form', + prompt: 'Add a contact form with name, email, message fields and a submit button', + }, + { + icon: 'Frame', + text: 'Create a navbar', + prompt: 'Build a responsive navigation bar with logo and menu items', + }, + ], + [ChatType.EDIT]: [ + { + icon: 'Pencil', + text: 'Change the button color', + prompt: 'Make the selected button blue with white text', + }, + { + icon: 'Size', + text: 'Adjust spacing', + prompt: 'Increase the padding around the selected element', + }, + { + icon: 'Text', + text: 'Update text content', + prompt: 'Change the heading text to say "Welcome to Our Platform"', + }, + ], + [ChatType.ASK]: [ + { + icon: 'QuestionMarkCircled', + text: 'Explain this component', + prompt: 'What does this component do and how does it work?', + }, + { + icon: 'Code', + text: 'Show me the structure', + prompt: 'Explain the component hierarchy and data flow', + }, + { + icon: 'InfoCircled', + text: 'Suggest improvements', + prompt: 'What improvements can I make to this component?', + }, + ], + [ChatType.FIX]: [ + { + icon: 'ExclamationTriangle', + text: 'Fix layout issues', + prompt: 'Fix any layout or alignment problems in the selected element', + }, + { + icon: 'CrossCircled', + text: 'Debug errors', + prompt: 'Find and fix any errors or warnings in the code', + }, + { + icon: 'Reset', + text: 'Optimize performance', + prompt: 'Improve the performance and remove any unnecessary code', + }, + ], +}; + +interface ExamplePromptsProps { + onSelectPrompt: (prompt: string, type: ChatType) => void; +} + +export const ExamplePrompts = observer(({ onSelectPrompt }: ExamplePromptsProps) => { + const editorEngine = useEditorEngine(); + const chatMode = editorEngine.state.chatMode; + const prompts = EXAMPLE_PROMPTS[chatMode] || EXAMPLE_PROMPTS[ChatType.EDIT]; + + const getModeTitle = () => { + switch (chatMode) { + case ChatType.CREATE: + return 'Create something new'; + case ChatType.EDIT: + return 'Edit your design'; + case ChatType.ASK: + return 'Ask about your code'; + case ChatType.FIX: + return 'Fix issues'; + default: + return 'Try an example'; + } + }; + + return ( +
+ +

+ {getModeTitle()} +

+

+ Start a conversation or try one of these examples +

+
+ {prompts.map((example, index) => { + const IconComponent = Icons[example.icon]; + return ( + + ); + })} +
+
+ ); +}); diff --git a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx index 8b08188af3..1c6baee28a 100644 --- a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx +++ b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx @@ -16,11 +16,13 @@ import { useTranslations } from 'next-intl'; import { useCallback } from 'react'; import { AssistantMessage } from './assistant-message'; import { ErrorMessage } from './error-message'; +import { ExamplePrompts } from './example-prompts'; import { UserMessage } from './user-message'; interface ChatMessagesProps { messages: ChatMessage[]; onEditMessage: EditMessage; + onSendMessage: (content: string, type: import('@onlook/models').ChatType) => void; isStreaming: boolean; error?: Error; } @@ -28,6 +30,7 @@ interface ChatMessagesProps { export const ChatMessages = observer(({ messages, onEditMessage, + onSendMessage, isStreaming, error, }: ChatMessagesProps) => { @@ -64,12 +67,7 @@ export const ChatMessages = observer(({ if (!messages || messages.length === 0) { return ( !editorEngine.elements.selected.length && ( -
- -

- {t(transKeys.editor.panels.edit.tabs.chat.emptyState)} -

-
+ ) ); } diff --git a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx index 656d5573b5..17d34a6e41 100644 --- a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx +++ b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx @@ -17,6 +17,8 @@ const MessageContentComponent = ({ isStream: boolean; }) => { let lastIncompleteToolIndex = -1; + let lastToolIndex = -1; + if (isStream) { for (let i = parts.length - 1; i >= 0; i--) { const part = parts[i]; @@ -30,6 +32,15 @@ const MessageContentComponent = ({ } } + // Find the last tool call (for auto-expand) + for (let i = parts.length - 1; i >= 0; i--) { + const part = parts[i]; + if (part?.type.startsWith('tool-')) { + lastToolIndex = i; + break; + } + } + const renderedParts = parts.map((part, idx) => { if (part?.type === 'text') { return ( @@ -41,6 +52,7 @@ const MessageContentComponent = ({ } else if (part?.type.startsWith('tool-')) { const toolPart = part as ToolUIPart;// Only show loading animation for the last incomplete tool call const isLoadingThisTool = isStream && idx === lastIncompleteToolIndex; + const isLatestTool = idx === lastToolIndex; return ( ); } else if (part?.type === 'reasoning') { diff --git a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/tool-call-display.tsx b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/tool-call-display.tsx index 03906dcf42..40b18dbc17 100644 --- a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/tool-call-display.tsx +++ b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/tool-call-display.tsx @@ -13,12 +13,14 @@ const ToolCallDisplayComponent = ({ messageId, toolPart, isStream, - applied + applied, + isLatest = false, }: { messageId: string, toolPart: ToolUIPart, isStream: boolean, - applied: boolean + applied: boolean, + isLatest?: boolean, }) => { const toolName = toolPart.type.split('-')[1]; @@ -91,6 +93,7 @@ const ToolCallDisplayComponent = ({ applied={applied} isStream={isStream} branchId={branchId} + defaultOpen={isLatest} /> ); } @@ -116,6 +119,7 @@ const ToolCallDisplayComponent = ({ applied={applied} isStream={isStream} branchId={branchId} + defaultOpen={isLatest} /> ); } @@ -141,6 +145,7 @@ const ToolCallDisplayComponent = ({ applied={applied} isStream={isStream} branchId={branchId} + defaultOpen={isLatest} /> ); } @@ -166,6 +171,7 @@ const ToolCallDisplayComponent = ({ applied={applied} isStream={isStream} branchId={branchId} + defaultOpen={isLatest} /> ); } diff --git a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx index 6080dc8e9f..1658ad4317 100644 --- a/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx +++ b/apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx @@ -28,6 +28,7 @@ export const ChatTabContent = ({ isStreaming={isStreaming} error={error} onEditMessage={editMessage} + onSendMessage={sendMessage} /> { const editorEngine = useEditorEngine(); - const [isOpen, setIsOpen] = useState(false); + const [isOpen, setIsOpen] = useState(defaultOpen); const [copied, setCopied] = useState(false); const copyToClipboard = () => { diff --git a/apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx b/apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx index 2668d5f949..d513a98e64 100644 --- a/apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx +++ b/apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx @@ -19,6 +19,7 @@ import { BranchDisplay } from './branch'; import { ModeToggle } from './mode-toggle'; import { ProjectBreadcrumb } from './project-breadcrumb'; import { PublishButton } from './publish'; +import { SaveIndicator } from './save-indicator'; export const TopBar = observer(() => { const stateManager = useStateManager(); @@ -50,6 +51,7 @@ export const TopBar = observer(() => {
+
diff --git a/apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx b/apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx new file mode 100644 index 0000000000..da44623d67 --- /dev/null +++ b/apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx @@ -0,0 +1,62 @@ +'use client'; + +import { useEditorEngine } from '@/components/store/editor'; +import type { SaveState } from '@/components/store/editor/save-state'; +import { Icons } from '@onlook/ui/icons'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip'; +import { observer } from 'mobx-react-lite'; + +export const SaveIndicator = observer(() => { + const editorEngine = useEditorEngine(); + const saveState: SaveState = editorEngine.saveState.saveState; + + const getIndicatorContent = () => { + switch (saveState) { + case 'saving': + return ( +
+ + Saving... +
+ ); + case 'saved': + return ( +
+ + Saved +
+ ); + case 'unsaved': + return ( +
+ + Unsaved changes +
+ ); + } + }; + + const getTooltipContent = () => { + switch (saveState) { + case 'saving': + return 'Saving your changes...'; + case 'saved': + return `Last saved ${editorEngine.saveState.formattedTimeSinceLastSave}`; + case 'unsaved': + return 'You have unsaved changes'; + } + }; + + return ( + + +
+ {getIndicatorContent()} +
+
+ +

{getTooltipContent()}

+
+
+ ); +}); diff --git a/apps/web/client/src/components/store/editor/code/index.ts b/apps/web/client/src/components/store/editor/code/index.ts index ac54a761e8..55c8249a9e 100644 --- a/apps/web/client/src/components/store/editor/code/index.ts +++ b/apps/web/client/src/components/store/editor/code/index.ts @@ -24,6 +24,7 @@ export class CodeManager { } async write(action: Action) { + this.editorEngine.saveState.startSaving(); try { // TODO: This is a hack to write code, we should refactor this if (action.type === 'write-code' && action.diffs[0]) { @@ -36,12 +37,14 @@ export class CodeManager { const requests = await this.collectRequests(action); await this.writeRequest(requests); } + this.editorEngine.saveState.debouncedCompleteSave(); } catch (error) { console.error('Error writing requests:', error); toast.error('Error writing requests', { description: error instanceof Error ? error.message : 'Unknown error', }); this.editorEngine.branches.activeError.addCodeApplicationError(error instanceof Error ? error.message : 'Unknown error', action); + this.editorEngine.saveState.markUnsaved(); } } diff --git a/apps/web/client/src/components/store/editor/engine.ts b/apps/web/client/src/components/store/editor/engine.ts index 4275fc237a..1987dd7bde 100644 --- a/apps/web/client/src/components/store/editor/engine.ts +++ b/apps/web/client/src/components/store/editor/engine.ts @@ -29,6 +29,7 @@ import { StateManager } from './state'; import { StyleManager } from './style'; import { TextEditingManager } from './text'; import { ThemeManager } from './theme'; +import { SaveStateManager } from './save-state'; export class EditorEngine { readonly projectId: string; @@ -71,6 +72,7 @@ export class EditorEngine { readonly snap: SnapManager = new SnapManager(this); readonly api: ApiManager = new ApiManager(this); readonly ide: IdeManager = new IdeManager(this); + readonly saveState: SaveStateManager = new SaveStateManager(this); constructor(projectId: string, posthog: PostHog) { this.projectId = projectId; @@ -114,6 +116,7 @@ export class EditorEngine { this.frameEvent.clear(); this.screenshot.clear(); this.snap.hideSnapLines(); + this.saveState.clear(); } clearUI() { diff --git a/apps/web/client/src/components/store/editor/save-state/index.ts b/apps/web/client/src/components/store/editor/save-state/index.ts new file mode 100644 index 0000000000..a9b8e6a08b --- /dev/null +++ b/apps/web/client/src/components/store/editor/save-state/index.ts @@ -0,0 +1,85 @@ +import { makeAutoObservable } from 'mobx'; +import type { EditorEngine } from '@/components/store/editor/engine'; + +export type SaveState = 'saved' | 'saving' | 'unsaved'; + +export class SaveStateManager { + saveState: SaveState = 'saved'; + private saveTimeout: NodeJS.Timeout | null = null; + private lastSaveTime: number = Date.now(); + + constructor(private editorEngine: EditorEngine) { + makeAutoObservable(this); + } + + /** + * Mark that a save operation has started + */ + startSaving() { + this.saveState = 'saving'; + if (this.saveTimeout) { + clearTimeout(this.saveTimeout); + this.saveTimeout = null; + } + } + + /** + * Mark that a save operation has completed successfully + */ + completeSave() { + this.saveState = 'saved'; + this.lastSaveTime = Date.now(); + } + + /** + * Mark that there are unsaved changes + */ + markUnsaved() { + // Only mark as unsaved if we're not currently saving + if (this.saveState !== 'saving') { + this.saveState = 'unsaved'; + } + } + + /** + * Debounced save completion - waits for a brief period after save + * to ensure no additional writes are happening + */ + debouncedCompleteSave(delay: number = 300) { + if (this.saveTimeout) { + clearTimeout(this.saveTimeout); + } + + this.saveTimeout = setTimeout(() => { + this.completeSave(); + this.saveTimeout = null; + }, delay); + } + + /** + * Get time since last save in seconds + */ + get timeSinceLastSave(): number { + return Math.floor((Date.now() - this.lastSaveTime) / 1000); + } + + /** + * Get formatted time since last save (e.g., "2 seconds ago", "1 minute ago") + */ + get formattedTimeSinceLastSave(): string { + const seconds = this.timeSinceLastSave; + if (seconds < 60) { + return seconds === 1 ? '1 second ago' : `${seconds} seconds ago`; + } + const minutes = Math.floor(seconds / 60); + return minutes === 1 ? '1 minute ago' : `${minutes} minutes ago`; + } + + clear() { + if (this.saveTimeout) { + clearTimeout(this.saveTimeout); + this.saveTimeout = null; + } + this.saveState = 'saved'; + } +} From a991f7b39f4924aef75bc32f7c31642fbbdef6ef Mon Sep 17 00:00:00 2001 From: Paritosh Kulkarni Date: Mon, 15 Dec 2025 14:08:38 -0800 Subject: [PATCH 2/3] docs: add comprehensive codebase architecture documentation --- .gitignore | 1 + ARCHITECTURE.md | 927 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 928 insertions(+) create mode 100644 ARCHITECTURE.md diff --git a/.gitignore b/.gitignore index 86919b0147..8db209e35b 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ mise.toml # Temporary files .tmp/ +.claude/ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000000..a4e7837dc5 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,927 @@ +# Onlook Codebase Architecture Map + +> **Project**: Onlook - Visual-First Code Editor for Next.js + TailwindCSS +> **Type**: Bun Monorepo +> **Tech Stack**: Next.js, tRPC, Supabase, Drizzle ORM, AI SDK, CodeSandbox SDK + +## ๐ŸŽฏ High-Level Overview + +Onlook is a **"Cursor for Designers"** - a visual-first code editor that allows users to create, edit, and deploy Next.js + TailwindCSS applications using AI and visual editing tools. It combines: + +- **Visual Editor**: Figma-like UI for editing React components in real-time +- **AI Chat**: Powered by OpenRouter/Morph/Relace for code generation +- **Sandbox Environment**: CodeSandbox SDK for running user projects +- **Deployment**: Freestyle hosting for instant deployment +- **Collaboration**: Real-time editing and branching + +--- + +## ๐Ÿ“ Monorepo Structure + +```mermaid +graph TB + subgraph "Onlook Monorepo" + subgraph "Apps Layer" + WEB["๐ŸŒ apps/web
Main Web Application"] + BACKEND["โš™๏ธ apps/backend
Supabase Backend"] + ADMIN["๐Ÿ‘ค apps/admin
Admin Dashboard"] + DOCS["๐Ÿ“š docs
Documentation Site"] + end + + subgraph "Core Packages" + AI["๐Ÿค– @onlook/ai
AI/LLM Integration"] + DB["๐Ÿ—„๏ธ @onlook/db
Database Schema"] + MODELS["๐Ÿ“ฆ @onlook/models
Shared Models"] + PARSER["๐Ÿ” @onlook/parser
Code Parser"] + CODE["๐Ÿ’พ @onlook/code-provider
Code Provider"] + end + + subgraph "Feature Packages" + GITHUB["๐Ÿ™ @onlook/github
GitHub Integration"] + GIT["๐Ÿ”€ @onlook/git
Git Operations"] + STRIPE["๐Ÿ’ณ @onlook/stripe
Payment Processing"] + EMAIL["๐Ÿ“ง @onlook/email
Email Service"] + GROWTH["๐Ÿ“ˆ @onlook/growth
Analytics"] + end + + subgraph "Infrastructure Packages" + UI["๐ŸŽจ @onlook/ui
Component Library"] + RPC["๐Ÿ”Œ @onlook/rpc
tRPC Definitions"] + FS["๐Ÿ“‚ @onlook/file-system
File Operations"] + IMG["๐Ÿ–ผ๏ธ @onlook/image-server
Image Processing"] + PENPAL["๐Ÿ‘ฅ @onlook/penpal
IFrame Communication"] + end + + subgraph "Utilities" + TYPES["๐Ÿ“ @onlook/types
TypeScript Types"] + CONST["โš™๏ธ @onlook/constants
Constants"] + UTIL["๐Ÿ”ง @onlook/utility
Utility Functions"] + FONTS["๐Ÿ”ค @onlook/fonts
Font Assets"] + end + + subgraph "Tooling" + ESLINT["๐Ÿ” @onlook/eslint
Linting Config"] + TS["๐Ÿ“˜ @onlook/typescript
TS Config"] + PRETTIER["โœจ @onlook/prettier
Prettier Config"] + SCRIPTS["๐Ÿ“œ @onlook/scripts
Build Scripts"] + end + end + + WEB --> AI + WEB --> DB + WEB --> MODELS + WEB --> PARSER + WEB --> CODE + WEB --> GITHUB + WEB --> STRIPE + WEB --> EMAIL + WEB --> GROWTH + WEB --> UI + WEB --> RPC + WEB --> FS + WEB --> IMG + WEB --> PENPAL + WEB --> TYPES + WEB --> CONST + WEB --> UTIL + WEB --> FONTS + + BACKEND --> DB + ADMIN --> DB + DOCS --> UI + + AI --> MODELS + AI --> UI + PARSER --> MODELS + CODE --> MODELS + DB --> STRIPE + + style WEB fill:#ff6b6b + style AI fill:#4ecdc4 + style DB fill:#45b7d1 + style PARSER fill:#f9ca24 + style UI fill:#6c5ce7 +``` + +--- + +## ๐Ÿ—๏ธ Web Application Architecture (apps/web) + +The main web application is split into **3 distinct parts**: + +### 1. **Client** (`apps/web/client`) +Next.js 16 application with App Router + +```mermaid +graph LR + subgraph "Web Client Structure" + subgraph "Frontend (client/src)" + APP["app/
Next.js App Router"] + COMP["components/
React Components"] + HOOKS["hooks/
Custom Hooks"] + TRPC["trpc/
tRPC Client"] + UTILS["utils/
Utilities"] + SERVICES["services/
Business Logic"] + STORIES["stories/
Storybook"] + end + + subgraph "Key Routes" + LANDING["page.tsx
Landing Page"] + PROJECTS["projects/
Project List"] + PROJECT["project/[id]/
Project Editor"] + AUTH["auth/
Authentication"] + API["api/
API Routes"] + end + + subgraph "Server Components" + SERVER["server/
Server Actions"] + ACTIONS["actions/
tRPC Actions"] + end + end + + APP --> LANDING + APP --> PROJECTS + APP --> PROJECT + APP --> AUTH + APP --> API + + COMP --> TRPC + TRPC --> SERVER + SERVER --> ACTIONS + + style PROJECT fill:#ff6b6b + style TRPC fill:#4ecdc4 + style SERVER fill:#45b7d1 +``` + +### 2. **Server** (`apps/web/server`) +tRPC server for API endpoints + +### 3. **Preload** (`apps/web/preload`) +Preload scripts for sandboxed environments + +--- + +## ๐Ÿง  Project Editor Architecture + +The core of Onlook - where users visually edit their projects: + +```mermaid +graph TB + subgraph "Project Editor (project/[id])" + EDITOR["Editor Canvas
(iFrame with user's Next.js app)"] + SIDEBAR["Left Sidebar
(Layers, Components, Pages)"] + TOOLBAR["Top Toolbar
(Text, Layout, Styling)"] + CHAT["AI Chat Panel
(AI-powered edits)"] + CODE["Code Panel
(Real-time code view)"] + CLI["CLI Terminal
(Command execution)"] + BRANCH["Branch Manager
(Git-like branching)"] + end + + subgraph "Data Flow" + SANDBOX["CodeSandbox Container
(Runs user's app)"] + PARSER_SVC["Parser Service
(AST manipulation)"] + AI_SVC["AI Service
(LLM integration)"] + FS_SVC["File System Service
(CRUD operations)"] + GIT_SVC["Git Service
(Version control)"] + end + + EDITOR --> SANDBOX + EDITOR --> PARSER_SVC + CHAT --> AI_SVC + CODE --> FS_SVC + BRANCH --> GIT_SVC + TOOLBAR --> PARSER_SVC + SIDEBAR --> PARSER_SVC + + PARSER_SVC --> FS_SVC + AI_SVC --> FS_SVC + FS_SVC --> SANDBOX + + style EDITOR fill:#ff6b6b + style CHAT fill:#4ecdc4 + style SANDBOX fill:#f9ca24 +``` + +--- + +## ๐Ÿค– AI System Architecture + +```mermaid +graph TB + subgraph "AI Package (@onlook/ai)" + subgraph "Agents" + CREATE_AGENT["Create Agent
(Generate new projects)"] + EDIT_AGENT["Edit Agent
(Modify existing code)"] + CHAT_AGENT["Chat Agent
(Conversational AI)"] + end + + subgraph "Tools" + CREATE_FILE["Create File Tool"] + EDIT_CODE["Edit Code Tool"] + RUN_CMD["Run Command Tool"] + READ_FILE["Read File Tool"] + DEPLOY["Deploy Tool"] + GIT_OPS["Git Operations Tool"] + SEARCH["Search Tool"] + IMAGE_GEN["Image Gen Tool"] + end + + subgraph "Contexts" + PROJECT_CTX["Project Context
(File structure, deps)"] + CODE_CTX["Code Context
(Current file, AST)"] + CONV_CTX["Conversation Context
(Chat history)"] + DESIGN_CTX["Design Context
(Brand, tokens)"] + end + + subgraph "Providers" + OPENROUTER["OpenRouter
(GPT-4, Claude, etc.)"] + MORPH["Morph Fast Apply
(Code editing)"] + RELACE["Relace
(Code editing)"] + OPENAI["OpenAI
(GPT models)"] + end + + subgraph "Apply System" + FAST_APPLY["Fast Apply
(Quick code edits)"] + STREAMING["Streaming Apply
(Real-time edits)"] + end + end + + CREATE_AGENT --> OPENROUTER + EDIT_AGENT --> MORPH + EDIT_AGENT --> RELACE + CHAT_AGENT --> OPENROUTER + + CREATE_AGENT --> CREATE_FILE + CREATE_AGENT --> RUN_CMD + EDIT_AGENT --> EDIT_CODE + EDIT_AGENT --> READ_FILE + CHAT_AGENT --> SEARCH + CHAT_AGENT --> IMAGE_GEN + + CREATE_AGENT --> PROJECT_CTX + EDIT_AGENT --> CODE_CTX + CHAT_AGENT --> CONV_CTX + + EDIT_CODE --> FAST_APPLY + EDIT_CODE --> STREAMING + + style EDIT_AGENT fill:#4ecdc4 + style MORPH fill:#f9ca24 + style FAST_APPLY fill:#ff6b6b +``` + +--- + +## ๐Ÿ”ง Parser System Architecture + +The parser is responsible for understanding and modifying React/TSX code: + +```mermaid +graph TB + subgraph "Parser Package (@onlook/parser)" + subgraph "Code Editing" + AST["AST Parser
(Babel/SWC)"] + TEMPLATE["Template Node
(JSX manipulation)"] + PRETTIER["Prettier
(Code formatting)"] + end + + subgraph "Operations" + INSERT["Insert Element"] + UPDATE["Update Props"] + DELETE["Delete Element"] + MOVE["Move Element"] + STYLE["Update Styles
(Tailwind classes)"] + end + + subgraph "Analysis" + IDS["Element IDs
(Track elements)"] + PACKAGES["Package Detection"] + HELPERS["Helper Functions"] + end + end + + AST --> INSERT + AST --> UPDATE + AST --> DELETE + AST --> MOVE + AST --> STYLE + + TEMPLATE --> INSERT + TEMPLATE --> UPDATE + + PRETTIER --> INSERT + PRETTIER --> UPDATE + PRETTIER --> STYLE + + IDS --> TEMPLATE + PACKAGES --> AST + + style AST fill:#f9ca24 + style TEMPLATE fill:#4ecdc4 + style STYLE fill:#ff6b6b +``` + +--- + +## ๐Ÿ—„๏ธ Database Architecture + +```mermaid +graph LR + subgraph "Database (@onlook/db + apps/backend)" + subgraph "Schema (Drizzle)" + USERS["users
(User accounts)"] + PROJECTS["projects
(User projects)"] + BRANCHES["branches
(Git branches)"] + DOMAINS["domains
(Custom domains)"] + TEAMS["teams
(Team collaboration)"] + SUBS["subscriptions
(Stripe billing)"] + USAGE["usage
(API usage tracking)"] + end + + subgraph "Supabase Backend" + AUTH["Authentication
(Email, OAuth)"] + STORAGE["Storage
(Project files, assets)"] + REALTIME["Realtime
(Collaboration)"] + EDGE["Edge Functions
(API endpoints)"] + end + + subgraph "Mappers" + USER_MAP["User Mapper"] + PROJECT_MAP["Project Mapper"] + BRANCH_MAP["Branch Mapper"] + end + end + + USERS --> AUTH + PROJECTS --> STORAGE + BRANCHES --> REALTIME + + USERS --> USER_MAP + PROJECTS --> PROJECT_MAP + BRANCHES --> BRANCH_MAP + + SUBS -.Stripe.-> USAGE + + style AUTH fill:#4ecdc4 + style STORAGE fill:#45b7d1 + style REALTIME fill:#ff6b6b +``` + +--- + +## ๐Ÿ”„ Data Flow: User Makes a Visual Edit + +```mermaid +sequenceDiagram + participant User + participant Editor as Editor Canvas (iFrame) + participant Penpal as Penpal (IFrame Bridge) + participant Parser as Parser Service + participant FS as File System + participant Sandbox as CodeSandbox + participant UI as User Interface + + User->>Editor: Click element, change style + Editor->>Penpal: Send element ID + new style + Penpal->>Parser: Request code modification + Parser->>FS: Read component file + FS-->>Parser: Return TSX content + Parser->>Parser: Parse AST, update Tailwind class + Parser->>FS: Write modified TSX + FS-->>Sandbox: Trigger hot reload + Sandbox-->>Editor: Re-render with new styles + Editor-->>UI: Update code panel + UI-->>User: Show updated code + preview + + Note over Parser: Uses @onlook/parser
to manipulate AST + Note over Sandbox: Uses CodeSandbox SDK
for isolated runtime +``` + +--- + +## ๐Ÿ”„ Data Flow: User Chats with AI + +```mermaid +sequenceDiagram + participant User + participant Chat as Chat Panel + participant AI as AI Service + participant Context as Context Builder + participant LLM as OpenRouter/Morph + participant Tools as AI Tools + participant FS as File System + participant Sandbox as CodeSandbox + + User->>Chat: "Add a contact form" + Chat->>AI: Send message + AI->>Context: Build context (project files, structure) + Context-->>AI: Return context + AI->>LLM: Request with context + tools + LLM-->>AI: Stream response with tool calls + AI->>Tools: Execute createFile("ContactForm.tsx") + Tools->>FS: Write new file + FS-->>Sandbox: Trigger reload + AI->>Tools: Execute editFile("app/page.tsx") + Tools->>FS: Update page to import form + FS-->>Sandbox: Trigger reload + Sandbox-->>Chat: Show preview + Chat-->>User: Stream response + preview + + Note over LLM: Uses GPT-4, Claude,
or other models + Note over Tools: Has 20+ tools:
create, edit, run, deploy, etc. +``` + +--- + +## ๐Ÿงฉ Package Dependency Graph + +```mermaid +graph TD + subgraph "Apps" + WEB_CLIENT["@onlook/web-client"] + WEB_SERVER["@onlook/web-server"] + BACKEND["@onlook/backend"] + end + + subgraph "Core Logic" + AI["@onlook/ai"] + PARSER["@onlook/parser"] + CODE_PROV["@onlook/code-provider"] + MODELS["@onlook/models"] + end + + subgraph "Data Layer" + DB["@onlook/db"] + RPC["@onlook/rpc"] + end + + subgraph "Integrations" + GITHUB["@onlook/github"] + GIT["@onlook/git"] + STRIPE["@onlook/stripe"] + EMAIL["@onlook/email"] + GROWTH["@onlook/growth"] + IMAGE["@onlook/image-server"] + end + + subgraph "UI & Utils" + UI["@onlook/ui"] + FS["@onlook/file-system"] + PENPAL["@onlook/penpal"] + UTIL["@onlook/utility"] + CONST["@onlook/constants"] + TYPES["@onlook/types"] + FONTS["@onlook/fonts"] + end + + WEB_CLIENT --> AI + WEB_CLIENT --> PARSER + WEB_CLIENT --> CODE_PROV + WEB_CLIENT --> MODELS + WEB_CLIENT --> DB + WEB_CLIENT --> RPC + WEB_CLIENT --> GITHUB + WEB_CLIENT --> STRIPE + WEB_CLIENT --> EMAIL + WEB_CLIENT --> GROWTH + WEB_CLIENT --> UI + WEB_CLIENT --> FS + WEB_CLIENT --> PENPAL + WEB_CLIENT --> UTIL + WEB_CLIENT --> CONST + WEB_CLIENT --> TYPES + WEB_CLIENT --> FONTS + WEB_CLIENT --> IMAGE + + WEB_SERVER --> RPC + WEB_SERVER --> DB + + BACKEND --> DB + + AI --> MODELS + AI --> UI + PARSER --> MODELS + CODE_PROV --> MODELS + DB --> STRIPE + + style WEB_CLIENT fill:#ff6b6b + style AI fill:#4ecdc4 + style PARSER fill:#f9ca24 + style DB fill:#45b7d1 + style UI fill:#6c5ce7 +``` + +--- + +## ๐Ÿ“ฆ Package Details + +### Core Packages + +#### **@onlook/ai** +- **Purpose**: AI/LLM integration layer +- **Key Features**: + - Agents: Create, Edit, Chat + - Tools: 20+ tools (create file, edit code, run command, deploy, etc.) + - Contexts: Project, Code, Conversation, Design + - Providers: OpenRouter, Morph, Relace, OpenAI + - Apply System: Fast Apply, Streaming Apply +- **Dependencies**: `ai`, `@openrouter/ai-sdk-provider`, `openai`, `zod`, `@onlook/ui`, `@onlook/models` + +#### **@onlook/parser** +- **Purpose**: Code parsing and manipulation +- **Key Features**: + - AST parsing (Babel/SWC) + - JSX/TSX template node manipulation + - Tailwind class editing + - Element ID tracking + - Prettier formatting +- **Dependencies**: `zod`, `@onlook/models` + +#### **@onlook/code-provider** +- **Purpose**: Abstraction layer for different code sources +- **Key Features**: + - Providers: CodeSandbox, Local, GitHub + - File CRUD operations + - Project management +- **Dependencies**: `@onlook/models` + +#### **@onlook/models** +- **Purpose**: Shared TypeScript models and Zod schemas +- **Key Features**: + - Project models + - Element models + - Chat models + - Code models +- **Dependencies**: `zod`, `ai` + +#### **@onlook/db** +- **Purpose**: Database schema and ORM +- **Key Features**: + - Drizzle ORM schemas + - Postgres connection + - Database migrations + - Seed scripts +- **Dependencies**: `drizzle-orm`, `postgres`, `pg`, `@onlook/stripe` + +### Feature Packages + +#### **@onlook/github** +- **Purpose**: GitHub integration +- **Key Features**: + - OAuth authentication + - Installation management + - Repository operations +- **Dependencies**: `octokit` + +#### **@onlook/git** +- **Purpose**: Git version control operations +- **Key Features**: + - Branching + - Commit management + - Diff generation + +#### **@onlook/stripe** +- **Purpose**: Payment and subscription management +- **Key Features**: + - Subscription plans + - Usage tracking + - Billing + +#### **@onlook/email** +- **Purpose**: Email service integration +- **Key Features**: + - Transactional emails + - Email templates + +#### **@onlook/growth** +- **Purpose**: Analytics and growth tracking +- **Key Features**: + - PostHog integration + - User tracking + - Feature flags + +#### **@onlook/image-server** +- **Purpose**: Image processing and optimization +- **Key Features**: + - Image resizing + - Format conversion + - CDN integration + +### Infrastructure Packages + +#### **@onlook/ui** +- **Purpose**: Shared UI component library +- **Key Features**: + - 70+ components + - Tailwind CSS v4 + - Theme system + - Hooks +- **Dependencies**: `react`, `tailwind-merge`, `class-variance-authority` + +#### **@onlook/rpc** +- **Purpose**: tRPC type definitions +- **Key Features**: + - Client-server type safety + - API route definitions +- **Dependencies**: `@trpc/client`, `@trpc/server` + +#### **@onlook/file-system** +- **Purpose**: File system operations +- **Key Features**: + - CRUD operations + - Path utilities + - File watching + +#### **@onlook/penpal** +- **Purpose**: IFrame communication bridge +- **Key Features**: + - Secure postMessage wrapper + - Bidirectional RPC +- **Dependencies**: `penpal` + +### Utility Packages + +#### **@onlook/types** +- **Purpose**: Shared TypeScript types + +#### **@onlook/constants** +- **Purpose**: Shared constants and configuration + +#### **@onlook/utility** +- **Purpose**: Utility functions + +#### **@onlook/fonts** +- **Purpose**: Font assets and loading + +--- + +## ๐Ÿš€ Runtime Architecture + +```mermaid +graph TB + subgraph "User's Browser" + NEXTJS["Next.js App
(onlook.com)"] + EDITOR["Visual Editor
(React components)"] + IFRAME["Sandboxed iFrame
(User's project)"] + end + + subgraph "CodeSandbox Container" + CONTAINER["Web Container
(Isolated runtime)"] + NEXTJS_USER["User's Next.js App
(Running in container)"] + HMR["Hot Module Reload
(Fast refresh)"] + end + + subgraph "Backend Services" + TRPC["tRPC Server
(apps/web/server)"] + SUPABASE["Supabase
(apps/backend)"] + STORAGE["Supabase Storage
(Project files)"] + end + + subgraph "External Services" + OPENROUTER["OpenRouter
(AI models)"] + MORPH["Morph
(Fast Apply)"] + STRIPE_SVC["Stripe
(Billing)"] + GITHUB_SVC["GitHub
(OAuth, repos)"] + FREESTYLE["Freestyle
(Hosting)"] + end + + NEXTJS --> EDITOR + EDITOR <--> IFRAME + IFRAME <--> CONTAINER + CONTAINER --> NEXTJS_USER + NEXTJS_USER --> HMR + + EDITOR --> TRPC + TRPC --> SUPABASE + SUPABASE --> STORAGE + + TRPC --> OPENROUTER + TRPC --> MORPH + TRPC --> STRIPE_SVC + TRPC --> GITHUB_SVC + TRPC --> FREESTYLE + + style CONTAINER fill:#f9ca24 + style TRPC fill:#4ecdc4 + style SUPABASE fill:#45b7d1 +``` + +--- + +## ๐ŸŽจ UI/UX Flow + +```mermaid +graph LR + subgraph "Landing Page" + HERO["Hero Section"] + FEATURES["Features"] + PRICING["Pricing"] + CTA["Sign Up CTA"] + end + + subgraph "Onboarding" + SIGNUP["Sign Up
(Email/GitHub)"] + CREATE["Create Project
(Template/AI/Import)"] + SETUP["Project Setup
(Name, framework)"] + end + + subgraph "Project List" + PROJECTS["My Projects"] + NEW["New Project"] + SEARCH["Search"] + end + + subgraph "Project Editor" + CANVAS["Visual Canvas"] + LEFT["Left Panel
(Layers, Components)"] + RIGHT["Right Panel
(Styles, Code)"] + BOTTOM["Bottom Panel
(Chat, CLI)"] + TOP["Top Toolbar
(Tools, Actions)"] + end + + subgraph "Deployment" + PREVIEW["Preview"] + DOMAIN["Custom Domain"] + DEPLOY_BTN["Deploy Button"] + SHARE["Share Link"] + end + + HERO --> CTA + CTA --> SIGNUP + SIGNUP --> CREATE + CREATE --> SETUP + SETUP --> PROJECTS + PROJECTS --> PROJECT_EDITOR + NEW --> CREATE + + CANVAS --> LEFT + CANVAS --> RIGHT + CANVAS --> BOTTOM + CANVAS --> TOP + + TOP --> DEPLOY_BTN + DEPLOY_BTN --> PREVIEW + PREVIEW --> DOMAIN + DOMAIN --> SHARE +``` + +--- + +## ๐Ÿ” Authentication & Authorization Flow + +```mermaid +sequenceDiagram + participant User + participant Frontend as Next.js Frontend + participant Supabase as Supabase Auth + participant DB as Database + participant GitHub as GitHub OAuth + + User->>Frontend: Click "Sign Up with GitHub" + Frontend->>Supabase: Initiate OAuth flow + Supabase->>GitHub: Redirect to GitHub + GitHub-->>User: Show authorization + User->>GitHub: Approve + GitHub-->>Supabase: Return auth code + Supabase->>GitHub: Exchange code for token + GitHub-->>Supabase: Return access token + Supabase->>DB: Create/update user record + Supabase-->>Frontend: Return session + JWT + Frontend->>Frontend: Store session in cookie + Frontend-->>User: Redirect to projects + + Note over Supabase: Uses @supabase/ssr
for server-side auth +``` + +--- + +## ๐ŸŒ Deployment Architecture + +```mermaid +graph TB + subgraph "Production Environment" + VERCEL["Vercel
(Next.js Frontend)"] + SUPABASE_PROD["Supabase
(Auth + DB + Storage)"] + FREESTYLE_HOST["Freestyle
(User Project Hosting)"] + end + + subgraph "CDN & Assets" + CDN["Vercel CDN
(Static assets)"] + IMAGES["Supabase Storage
(User images)"] + end + + subgraph "External APIs" + OPENROUTER_API["OpenRouter API"] + MORPH_API["Morph API"] + STRIPE_API["Stripe API"] + GITHUB_API["GitHub API"] + end + + VERCEL <--> SUPABASE_PROD + VERCEL --> CDN + VERCEL --> IMAGES + VERCEL --> OPENROUTER_API + VERCEL --> MORPH_API + VERCEL --> STRIPE_API + VERCEL --> GITHUB_API + + VERCEL --> FREESTYLE_HOST + + style VERCEL fill:#ff6b6b + style SUPABASE_PROD fill:#45b7d1 + style FREESTYLE_HOST fill:#4ecdc4 +``` + +--- + +## ๐Ÿ“Š Key Technologies Summary + +| Layer | Technology | Purpose | +|-------|-----------|---------| +| **Frontend** | Next.js 16 (App Router) | Server-side rendering, routing | +| **UI Framework** | React 19 | Component library | +| **Styling** | TailwindCSS v4 | Utility-first CSS | +| **Backend** | tRPC | Type-safe API | +| **Database** | Supabase (Postgres) | Auth, DB, Storage | +| **ORM** | Drizzle | Database queries | +| **AI** | AI SDK + OpenRouter | LLM integration | +| **Sandbox** | CodeSandbox SDK | Isolated runtime | +| **Hosting** | Freestyle | User project hosting | +| **Payments** | Stripe | Subscriptions | +| **Analytics** | PostHog | User tracking | +| **Monorepo** | Bun Workspaces | Package management | +| **Runtime** | Bun | Fast JavaScript runtime | +| **Language** | TypeScript 5.5+ | Type safety | + +--- + +## ๐Ÿ” Key File Locations + +### Configuration Files +- `/package.json` - Root monorepo config +- `/bunfig.toml` - Bun configuration +- `/docker-compose.yml` - Docker setup +- `/apps/web/client/next.config.ts` - Next.js config +- `/apps/web/client/tailwind.config.ts` - Tailwind config (likely in @onlook/ui) + +### Entry Points +- `/apps/web/client/src/app/layout.tsx` - Root layout +- `/apps/web/client/src/app/page.tsx` - Landing page +- `/apps/web/client/src/app/project/[id]/page.tsx` - Project editor +- `/apps/backend/supabase/` - Supabase backend + +### Core Logic +- `/packages/ai/src/agents/` - AI agents +- `/packages/ai/src/tools/` - AI tools +- `/packages/parser/src/code-edit/` - Code editing logic +- `/packages/models/src/` - Shared models +- `/apps/web/client/src/server/` - Server actions + +--- + +## ๐Ÿงช Testing & Development + +```bash +# Development +bun dev # Start web client + preload +bun dev:admin # Start admin dashboard +bun backend:start # Start Supabase backend +bun docs # Start documentation site + +# Building +bun build # Build web client +bun docker:build # Build Docker image + +# Database +bun db:gen # Generate Drizzle schema +bun db:push # Push schema to DB +bun db:seed # Seed database +bun db:migrate # Run migrations + +# Quality +bun lint # Lint all packages +bun format # Format code +bun typecheck # Type check +bun test # Run tests +``` + +--- + +## ๐ŸŽฏ Summary + +**Onlook** is a sophisticated monorepo with: +- **3 apps**: Web (Next.js), Backend (Supabase), Admin +- **20 packages**: AI, Parser, DB, UI, and more +- **4 tooling packages**: ESLint, TypeScript, Prettier, Scripts +- **Complex data flows**: Visual editing โ†’ Parser โ†’ File System โ†’ Sandbox โ†’ Re-render +- **AI-powered**: Multiple agents, 20+ tools, streaming edits +- **Full-stack**: Next.js + tRPC + Supabase + CodeSandbox + Freestyle + +The architecture is designed for: +1. **Real-time visual editing** of React components +2. **AI-powered code generation** with streaming responses +3. **Sandboxed execution** of user projects +4. **Collaborative editing** with branching +5. **Instant deployment** to production + From f8690b0a92c1f02fe9f11f091e66a1bbf30ea4bf Mon Sep 17 00:00:00 2001 From: Paritosh Kulkarni Date: Mon, 15 Dec 2025 14:10:12 -0800 Subject: [PATCH 3/3] fix: improve dev login error handling and suppress hydration warning - Add try-catch wrapper in DevLoginButton with toast error notification - Re-throw errors in AuthContext for proper error propagation - Add suppressHydrationWarning to body tag to fix Next.js warnings - Fix indentation in LoginButton component --- .../client/src/app/_components/login-button.tsx | 15 +++++++++++++-- apps/web/client/src/app/auth/auth-context.tsx | 2 ++ apps/web/client/src/app/layout.tsx | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/web/client/src/app/_components/login-button.tsx b/apps/web/client/src/app/_components/login-button.tsx index afc9f4a777..b9ef5625a4 100644 --- a/apps/web/client/src/app/_components/login-button.tsx +++ b/apps/web/client/src/app/_components/login-button.tsx @@ -58,7 +58,7 @@ export const LoginButton = ({ ) : ( icon )} -{t(transKeys.welcome.login[translationKey])} + {t(transKeys.welcome.login[translationKey])} {isLastSignInMethod && (

{t(transKeys.welcome.login.lastUsed)}

@@ -79,11 +79,22 @@ export const DevLoginButton = ({ const { handleDevLogin, signingInMethod } = useAuthContext(); const isSigningIn = signingInMethod === SignInMethod.DEV; + const handleClick = async () => { + try { + await handleDevLogin(returnUrl); + } catch (error) { + console.error('Error with dev login:', error); + toast.error('Dev login failed', { + description: error instanceof Error ? error.message : 'Please try again.', + }); + } + }; + return (