Skip to content

fix: improve dev login error handling and suppress hydration warning#3069

Open
paritoshk wants to merge 3 commits intoonlook-dev:mainfrom
paritoshk:fix/dev-login-error-handling
Open

fix: improve dev login error handling and suppress hydration warning#3069
paritoshk wants to merge 3 commits intoonlook-dev:mainfrom
paritoshk:fix/dev-login-error-handling

Conversation

@paritoshk
Copy link

@paritoshk paritoshk commented Dec 15, 2025

Problem: Dev login failures show no user feedback and Next.js hydration warnings appear in console.

Solution:

  • 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

Testing:

  • Successful dev login flow works
  • Failed dev login shows clear error message
  • No hydration warnings in console

User Experience: Silent failure replaced with clear error toast notifications


Important

Improves dev login error handling with toast notifications, suppresses Next.js hydration warnings, and adds a save state indicator for the editor.

  • Error Handling:
    • Add try-catch in DevLoginButton for error toast notifications on login failure.
    • Re-throw errors in AuthProvider for better error propagation.
  • Hydration Warning:
    • Add suppressHydrationWarning to <body> in layout.tsx to fix Next.js warnings.
  • Save State Indicator:
    • Introduce SaveIndicator component in top-bar/index.tsx to show save status.
    • Implement SaveStateManager in save-state/index.ts to manage save states.
  • Misc:
    • Fix indentation in LoginButton component.

This description was created by Ellipsis for f8690b0. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a save indicator in the top bar displaying save status (saving, saved, or unsaved changes) with helpful tooltips.
    • Introduced example prompts for each chat mode to help users get started with the chat functionality.
    • Latest tool calls now expand by default in chat messages for easier viewing.
  • Bug Fixes

    • Improved error handling for developer login with user-friendly feedback.
  • Documentation

    • Added comprehensive architecture documentation.

✏️ Tip: You can customize this high-level summary in your review settings.

paritoshk and others added 3 commits December 13, 2025 14:21
…to-expand)

Implements three high-impact UX improvements identified from user journey analysis:

## 1. Save State Indicator (onlook-dev#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 (onlook-dev#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 (onlook-dev#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 <[email protected]>
- 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
@vercel
Copy link

vercel bot commented Dec 15, 2025

@paritoshk is attempting to deploy a commit to the Onlook Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Dec 15, 2025

Walkthrough

This PR introduces a save-state management system for tracking code write operations, adds UI components for chat example prompts and save status indicators, improves error handling in authentication, and includes comprehensive architecture documentation. Multiple chat components are enhanced with prop propagation and tool call display improvements.

Changes

Cohort / File(s) Summary
Save State Management
apps/web/client/src/components/store/editor/save-state/index.ts
New SaveStateManager class that tracks save lifecycle ('saved', 'saving', 'unsaved'), provides debounced completion, timestamps, and formatted time-since-save output for observable state management.
EditorEngine Integration
apps/web/client/src/components/store/editor/engine.ts
Instantiates SaveStateManager as a public field and integrates cleanup into the clear() lifecycle.
Code Manager Integration
apps/web/client/src/components/store/editor/code/index.ts
Wires save-state transitions into CodeManager.write: calls startSaving() at start, debouncedCompleteSave() on success, markUnsaved() on error.
Save Indicator UI
apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx, top-bar/index.tsx
New SaveIndicator component that reads saveState from editor engine and renders state-specific UI (icon/text) and tooltips; integrated into TopBar.
Example Prompts Feature
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
New ExamplePrompts component displaying mode-specific prompt suggestions with icons; renders as empty-state placeholder in chat.
Chat Messages Enhancements
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx, chat-tab-content/index.tsx
Added onSendMessage prop to ChatMessages and wired it through ChatTabContent; integrated ExamplePrompts rendering when no messages present.
Tool Call Display Improvements
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx, message-content/tool-call-display.tsx, code-display/collapsible-code-block.tsx
Tracking last tool call index and passing isLatest prop; added defaultOpen prop to ToolCallDisplay and CollapsibleCodeBlock to auto-expand latest code blocks.
Authentication Error Handling
apps/web/client/src/app/_components/login-button.tsx, app/auth/auth-context.tsx
Added try/catch error handling in LoginButton with toast notification; re-throws errors in handleDevLogin catch block for proper propagation.
Layout & Config
apps/web/client/src/app/layout.tsx, .gitignore, ARCHITECTURE.md
Added suppressHydrationWarning to body tag; added .claude/ to .gitignore; added comprehensive ARCHITECTURE.md documentation.

Sequence Diagram

sequenceDiagram
    participant User
    participant CodeManager
    participant SaveStateManager
    participant EditorStore
    participant SaveIndicator

    User->>CodeManager: write code
    CodeManager->>SaveStateManager: startSaving()
    SaveStateManager->>SaveStateManager: state = 'saving'
    SaveStateManager->>EditorStore: notify observers (saveState)
    EditorStore->>SaveIndicator: update
    SaveIndicator->>SaveIndicator: render spinner + "Saving..."

    CodeManager->>CodeManager: apply code changes
    CodeManager->>SaveStateManager: debouncedCompleteSave(300ms)
    
    par Debounce Wait
        SaveIndicator->>SaveIndicator: maintain saving state display
    end

    SaveStateManager->>SaveStateManager: (after 300ms) completeSave()
    SaveStateManager->>SaveStateManager: state = 'saved'<br/>lastSaveTime = now()
    SaveStateManager->>EditorStore: notify observers (saveState)
    EditorStore->>SaveIndicator: update
    SaveIndicator->>SaveIndicator: render check + "Saved"
    SaveIndicator->>SaveIndicator: tooltip: "Last saved 1 second ago"
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • SaveStateManager logic: Review the debounce implementation, timestamp formatting, and state transition logic carefully
  • Prop propagation chain: Verify onSendMessage and isLatest props flow correctly through ChatMessages → ChatTabContent → example prompts and tool displays
  • Observable state management: Ensure MobX observer wrapping and reactive updates work correctly across SaveIndicator and other connected components
  • Error handling in auth: Confirm error re-throwing and toast handling doesn't break existing flows
  • Integration points: CodeManager and EditorEngine modifications should be tested for side effects on existing save/write workflows

Possibly related PRs

Poem

🐰 A rabbit hops through save states with glee,
Now prompts bloom bright where messages be,
Spinning and saved with a blink and a glow,
Code flows like carrots, from quick to slow!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: dev login error handling improvements and hydration warning suppression. It is concise, specific, and directly reflects the primary objectives.
Description check ✅ Passed The description covers the problem statement, solution details, and testing approach comprehensively. It aligns well with the template structure and provides clear context for the changes, including a well-organized summary of error handling, hydration fixes, and the new save state indicator feature.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
apps/web/client/src/app/_components/login-button.tsx (1)

87-89: Consider using next-intl for user-facing text.

The hardcoded strings 'Dev login failed', 'Please try again.', and 'DEV MODE: Sign in as demo user' should ideally use next-intl messages for consistency with the rest of the codebase. The LoginButton component correctly uses t(transKeys.welcome.login[translationKey]) for its text.

Based on coding guidelines, hardcoded user-facing text should be avoided in favor of next-intl messages/hooks.

Also applies to: 102-102

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx (2)

95-108: Consider using next-intl for user-facing text.

The hardcoded strings in getModeTitle() and the UI text (lines 113-118) should use next-intl messages/hooks for internationalization consistency. This includes:

  • Mode titles: "Create something new", "Edit your design", etc.
  • Helper text: "Start a conversation or try one of these examples"

Based on coding guidelines, hardcoded user-facing text should be avoided.

Also applies to: 113-118


15-84: Consider extracting prompts to translation keys.

The EXAMPLE_PROMPTS constant contains hardcoded user-facing text for button labels and prompt content. For full i18n support, consider moving these strings to translation files.

That said, if these prompts are intentionally English-only for AI interaction purposes, this may be acceptable. Verify with the team whether these prompts should be localized.

apps/web/client/src/components/store/editor/save-state/index.ts (1)

8-8: Use browser-compatible timeout type.

NodeJS.Timeout is specific to Node.js and may not be the correct type in browser contexts. Use the return type of setTimeout for better cross-environment compatibility.

Apply this diff:

-    private saveTimeout: NodeJS.Timeout | null = null;
+    private saveTimeout: ReturnType<typeof setTimeout> | null = null;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6962f87 and f8690b0.

📒 Files selected for processing (16)
  • .gitignore (1 hunks)
  • ARCHITECTURE.md (1 hunks)
  • apps/web/client/src/app/_components/login-button.tsx (2 hunks)
  • apps/web/client/src/app/auth/auth-context.tsx (1 hunks)
  • apps/web/client/src/app/layout.tsx (1 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx (1 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx (2 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx (3 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/tool-call-display.tsx (5 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx (1 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx (1 hunks)
  • apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx (2 hunks)
  • apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx (1 hunks)
  • apps/web/client/src/components/store/editor/code/index.ts (2 hunks)
  • apps/web/client/src/components/store/editor/engine.ts (3 hunks)
  • apps/web/client/src/components/store/editor/save-state/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
apps/web/client/src/app/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/app/**/*.tsx: Default to Server Components; add 'use client' when using events, state/effects, browser APIs, or client‑only libraries
Do not use process.env in client code; import env from @/env instead

Avoid hardcoded user-facing text; use next-intl messages/hooks

Files:

  • apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
  • apps/web/client/src/app/auth/auth-context.tsx
  • apps/web/client/src/app/layout.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-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/_components/login-button.tsx
  • 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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx
  • apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
  • apps/web/client/src/app/auth/auth-context.tsx
  • apps/web/client/src/app/layout.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
  • apps/web/client/src/components/store/editor/code/index.ts
  • apps/web/client/src/components/store/editor/save-state/index.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-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/_components/login-button.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
  • apps/web/client/src/components/store/editor/engine.ts
  • 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/code-display/collapsible-code-block.tsx
  • apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
  • apps/web/client/src/app/auth/auth-context.tsx
  • apps/web/client/src/app/layout.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-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/_components/login-button.tsx
  • 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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx
  • apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
  • apps/web/client/src/app/auth/auth-context.tsx
  • apps/web/client/src/app/layout.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
  • apps/web/client/src/components/store/editor/code/index.ts
  • apps/web/client/src/components/store/editor/save-state/index.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-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/_components/login-button.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
  • apps/web/client/src/components/store/editor/engine.ts
  • 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/code-display/collapsible-code-block.tsx
  • apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx
apps/web/client/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Files:

  • apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
  • apps/web/client/src/app/auth/auth-context.tsx
  • apps/web/client/src/app/layout.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-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/_components/login-button.tsx
  • 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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx
  • apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
  • apps/web/client/src/app/auth/auth-context.tsx
  • apps/web/client/src/app/layout.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
  • apps/web/client/src/components/store/editor/code/index.ts
  • apps/web/client/src/components/store/editor/save-state/index.ts
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-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/_components/login-button.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
  • apps/web/client/src/components/store/editor/engine.ts
  • 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/code-display/collapsible-code-block.tsx
  • apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx
apps/web/client/src/app/layout.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Preserve dark theme defaults via ThemeProvider usage in the root layout

Preserve dark theme defaults via ThemeProvider in the root layout

Files:

  • apps/web/client/src/app/layout.tsx
apps/web/client/src/app/{page,layout,route}.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Follow App Router file conventions (page.tsx, layout.tsx, route.ts) within src/app

Files:

  • apps/web/client/src/app/layout.tsx
🧠 Learnings (12)
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/**/*.tsx : observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Applied to files:

  • apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/app/layout.tsx : Preserve dark theme defaults via ThemeProvider in the root layout

Applied to files:

  • apps/web/client/src/app/layout.tsx
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/app/layout.tsx : Preserve dark theme defaults via ThemeProvider usage in the root layout

Applied to files:

  • apps/web/client/src/app/layout.tsx
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/app/**/*.tsx : Avoid hardcoded user-facing text; use next-intl messages/hooks

Applied to files:

  • apps/web/client/src/app/layout.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx
  • apps/web/client/src/app/_components/login-button.tsx
  • 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/index.tsx
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/**/*.{ts,tsx} : Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Applied to files:

  • apps/web/client/src/app/layout.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx
  • apps/web/client/src/app/_components/login-button.tsx
  • 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/index.tsx
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/**/*.tsx : Create MobX store instances with useState(() => new Store()) for stable identities across renders

Applied to files:

  • apps/web/client/src/components/store/editor/save-state/index.ts
  • apps/web/client/src/components/store/editor/engine.ts
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/**/*.tsx : Create MobX store instances with useState(() => new Store()) for stable references across renders

Applied to files:

  • apps/web/client/src/components/store/editor/save-state/index.ts
  • apps/web/client/src/components/store/editor/engine.ts
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/**/*.tsx : Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)

Applied to files:

  • apps/web/client/src/components/store/editor/save-state/index.ts
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/**/*.tsx : Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races

Applied to files:

  • apps/web/client/src/components/store/editor/save-state/index.ts
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/**/*.tsx : Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

Applied to files:

  • apps/web/client/src/components/store/editor/save-state/index.ts
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/app/**/*.tsx : Default to Server Components; add 'use client' when using events, state/effects, browser APIs, or client‑only libraries

Applied to files:

  • apps/web/client/src/app/_components/login-button.tsx
  • ARCHITECTURE.md
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
Repo: onlook-dev/onlook PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/app/**/*.{ts,tsx} : Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Applied to files:

  • apps/web/client/src/app/_components/login-button.tsx
🧬 Code graph analysis (10)
apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx (3)
apps/web/client/src/components/store/editor/index.tsx (1)
  • useEditorEngine (10-14)
apps/web/client/src/components/store/editor/save-state/index.ts (1)
  • SaveState (4-4)
packages/ui/src/components/tooltip.tsx (3)
  • Tooltip (72-72)
  • TooltipTrigger (72-72)
  • TooltipContent (72-72)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx (2)
packages/ui/src/components/icons/index.tsx (1)
  • Icons (138-3667)
apps/web/client/src/components/store/editor/index.tsx (1)
  • useEditorEngine (10-14)
apps/web/client/src/components/store/editor/code/index.ts (1)
packages/ui/src/components/sonner.tsx (1)
  • toast (19-19)
apps/web/client/src/components/store/editor/save-state/index.ts (1)
apps/web/client/src/components/store/editor/engine.ts (1)
  • EditorEngine (34-138)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx (1)
apps/web/client/src/components/store/editor/chat/index.ts (1)
  • sendMessage (43-49)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx (1)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/tool-call-display.tsx (1)
  • ToolCallDisplay (236-236)
apps/web/client/src/components/store/editor/engine.ts (1)
apps/web/client/src/components/store/editor/save-state/index.ts (1)
  • SaveStateManager (6-85)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx (3)
packages/models/src/chat/message/message.ts (1)
  • ChatMessage (19-19)
apps/web/client/src/app/project/[id]/_hooks/use-chat/index.tsx (1)
  • EditMessage (19-23)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx (1)
  • ExamplePrompts (90-137)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx (1)
apps/web/client/src/components/store/editor/index.tsx (1)
  • useEditorEngine (10-14)
apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx (1)
apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx (1)
  • SaveIndicator (9-62)
🪛 LanguageTool
ARCHITECTURE.md

[uncategorized] ~569-~569: The official name of this software platform is spelled with a capital “H”.
Context: ...` ### Feature Packages #### @onlook/github - Purpose: GitHub integration - *...

(GITHUB)

🔇 Additional comments (18)
.gitignore (1)

48-50: Ignore entry for .claude/ looks good

Consistent with other temporary/tool directories and safe to keep out of version control.

apps/web/client/src/app/layout.tsx (1)

74-74: LGTM!

Adding suppressHydrationWarning to the <body> tag is appropriate for Next.js applications using ThemeProvider, as client-side theme modifications can cause legitimate hydration mismatches. This correctly addresses the console warnings mentioned in the PR objectives.

apps/web/client/src/app/auth/auth-context.tsx (1)

59-65: LGTM!

Re-throwing the error after logging makes handleDevLogin consistent with handleLogin (lines 44-46), allowing callers to implement their own error handling (e.g., displaying toast notifications). The finally block correctly ensures signingInMethod is reset regardless of success or failure.

apps/web/client/src/components/store/editor/engine.ts (1)

32-32: LGTM!

The SaveStateManager integration follows the established pattern used by other managers in EditorEngine. The import, property declaration, and cleanup in clear() are all consistent with how other managers (e.g., ChatManager, ActionManager) are handled.

Also applies to: 75-75, 119-119

apps/web/client/src/app/_components/login-button.tsx (1)

82-91: Error handling implementation looks good.

The try/catch wrapper with toast notification properly addresses the PR objective of providing user feedback on dev login failures. The pattern is consistent with LoginButton's handleLoginClick.

ARCHITECTURE.md (2)

569-575: Minor: Capitalize "GitHub" consistently.

Per the static analysis hint, "GitHub" should be capitalized with a capital "H" for consistency with official branding.

-#### **@onlook/github**
+#### **@onlook/GitHub**

Actually, the package name @onlook/github is correct as package names are typically lowercase. The issue is in the descriptive text if any. Looking at the content, the package name format is fine. This can be ignored.


113-113: No changes required. The ARCHITECTURE.md documentation correctly references Next.js 16, which matches the actual version specified in package.json (16.0.7).

Likely an incorrect or invalid review comment.

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx (1)

18-18: LGTM!

The defaultOpen prop is cleanly implemented with a sensible default of false, maintaining backward compatibility. Using useState(defaultOpen) correctly sets the initial state while allowing the component to be uncontrolled after mount.

Also applies to: 26-29

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx (1)

90-137: Component structure looks good.

The component correctly:

  • Uses 'use client' directive (required for hooks, events, and MobX observer)
  • Wraps with observer for MobX reactivity on chatMode
  • Properly accesses editor state via useEditorEngine()
  • Has a clean fallback with || EXAMPLE_PROMPTS[ChatType.EDIT]
apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx (1)

22-22: LGTM! SaveIndicator integration looks good.

The SaveIndicator component is properly imported and rendered in the appropriate location within the TopBar's right-aligned section.

Also applies to: 54-54

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-tab-content/index.tsx (1)

31-31: LGTM! Prop propagation is correct.

The onSendMessage prop is properly passed through to the ChatMessages component, enabling message sending from child components like example prompts.

apps/web/client/src/components/store/editor/code/index.ts (1)

27-27: LGTM! Save state lifecycle integration is correct.

The save state transitions are properly integrated:

  • startSaving() at the beginning of the write operation
  • debouncedCompleteSave() after successful writes
  • markUnsaved() on error paths

This provides clear user feedback about the save status throughout the operation.

Also applies to: 40-40, 47-47

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/tool-call-display.tsx (2)

16-17: LGTM! Optional prop with safe default.

The isLatest prop is properly defined with a default value of false, ensuring backward compatibility and clear intent.

Also applies to: 23-23


96-96: LGTM! Consistent propagation across tool types.

The defaultOpen={isLatest} prop is consistently passed to all relevant CollapsibleCodeBlock instances, ensuring uniform behavior across different tool call types.

Also applies to: 122-122, 148-148, 174-174

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx (1)

19-19: LGTM! ExamplePrompts integration enhances empty state UX.

The onSendMessage prop is properly added to the component interface and correctly wired to the ExamplePrompts component, improving the user experience when there are no chat messages.

Also applies to: 25-25, 33-33, 70-70

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx (1)

20-21: LGTM! Last tool call tracking logic is correct.

The implementation properly identifies the last tool call using a backward scan and passes this information to ToolCallDisplay for conditional auto-expansion. The logic is separate from the existing incomplete tool tracking, maintaining clear separation of concerns.

Also applies to: 35-42, 55-55, 63-63

apps/web/client/src/components/store/editor/save-state/index.ts (2)

18-24: Consider edge case: rapid successive saves.

The startSaving() method clears any pending timeout, which is correct. However, if multiple save operations are initiated in rapid succession, each call to startSaving() will cancel the previous debounce timer. This might keep the UI in a "saving" state longer than expected if saves keep happening.

This is likely the intended behavior, but verify that this aligns with the desired UX for rapid successive writes.


37-42: Verify the markUnsaved guard logic.

The guard if (this.saveState !== 'saving') prevents marking as unsaved during an active save. However, if an error occurs during save (e.g., in CodeManager.write), the state would be 'saving', and this guard would prevent marking it as 'unsaved'.

Looking at the CodeManager.write implementation, it calls markUnsaved() in the error path after the save has failed. At that point, is the state still 'saving'? If so, this guard might prevent proper error state indication.

Verify the call sequence:

  1. Does startSaving() get called before the try block?
  2. When an error occurs, is the state still 'saving' when markUnsaved() is called?
  3. Should the error path first reset the state before marking unsaved?

Consider this safer pattern:

markUnsaved() {
    if (this.saveTimeout) {
        clearTimeout(this.saveTimeout);
        this.saveTimeout = null;
    }
    this.saveState = 'unsaved';
}

return (
<div className="flex items-center gap-1.5 text-xs text-foreground-secondary">
<Icons.Reload className="h-3 w-3 animate-spin" />
<span>Saving...</span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded text with next-intl messages.

The component contains hardcoded user-facing text ("Saving...", "Saved", "Unsaved changes") that should be internationalized using next-intl messages and hooks.

As per coding guidelines: "Avoid hardcoded user-facing text; use next-intl messages/hooks instead"

Apply this pattern to internationalize the strings:

+import { useTranslations } from 'next-intl';
+import { transKeys } from '@/i18n/keys';
+
 export const SaveIndicator = observer(() => {
     const editorEngine = useEditorEngine();
+    const t = useTranslations();
     const saveState: SaveState = editorEngine.saveState.saveState;

     const getIndicatorContent = () => {
         switch (saveState) {
             case 'saving':
                 return (
                     <div className="flex items-center gap-1.5 text-xs text-foreground-secondary">
                         <Icons.Reload className="h-3 w-3 animate-spin" />
-                        <span>Saving...</span>
+                        <span>{t(transKeys.editor.saveState.saving)}</span>
                     </div>
                 );
             case 'saved':
                 return (
                     <div className="flex items-center gap-1.5 text-xs text-foreground-tertiary">
                         <Icons.Check className="h-3 w-3" />
-                        <span>Saved</span>
+                        <span>{t(transKeys.editor.saveState.saved)}</span>
                     </div>
                 );
             case 'unsaved':
                 return (
                     <div className="flex items-center gap-1.5 text-xs text-orange-500">
                         <Icons.Circle className="h-3 w-3 fill-current" />
-                        <span>Unsaved changes</span>
+                        <span>{t(transKeys.editor.saveState.unsaved)}</span>
                     </div>
                 );
         }
     };

Based on coding guidelines.

Also applies to: 26-26, 33-33

🤖 Prompt for AI Agents
In apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
around lines 19, 26 and 33, the user-facing strings ("Saving...", "Saved",
"Unsaved changes") are hardcoded and must be internationalized: import and use
next-intl's useTranslations (or the project's i18n hook), add message keys
(e.g., project.saveIndicator.saving, .saved, .unsaved) to the appropriate locale
JSON, and replace the literal strings with calls to
t('project.saveIndicator.saving') / t('project.saveIndicator.saved') /
t('project.saveIndicator.unsaved') so the component renders localized text;
ensure the import and t() usage match the component's typing and render logic.

const getTooltipContent = () => {
switch (saveState) {
case 'saving':
return 'Saving your changes...';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded tooltip text with next-intl messages.

The tooltip text is also hardcoded and should be internationalized. Note that the "Last saved" message includes dynamic content that needs proper formatting.

Apply this pattern:

     const getTooltipContent = () => {
         switch (saveState) {
             case 'saving':
-                return 'Saving your changes...';
+                return t(transKeys.editor.saveState.savingTooltip);
             case 'saved':
-                return `Last saved ${editorEngine.saveState.formattedTimeSinceLastSave}`;
+                return t(transKeys.editor.saveState.savedTooltip, {
+                    time: editorEngine.saveState.formattedTimeSinceLastSave
+                });
             case 'unsaved':
-                return 'You have unsaved changes';
+                return t(transKeys.editor.saveState.unsavedTooltip);
         }
     };

Based on coding guidelines.

Also applies to: 44-44, 46-46

🤖 Prompt for AI Agents
In apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
around lines 42, 44 and 46, replace the three hardcoded tooltip strings with
next-intl message lookups and use the intl/date formatting helper for the
dynamic "Last saved" string; add message keys (e.g. saveIndicator.saving,
saveIndicator.savedLast, saveIndicator.saved) to the locale JSON, import/use the
next-intl hook (useTranslations or intl) in this component, use the translation
for the static tooltips and for the "Last saved" message call the translation
with a formatted date/time argument (use the provided date formatter from
next-intl or formatDate) so the dynamic timestamp is localized correctly.

Comment on lines +69 to +76
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`;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Internationalize time formatting strings.

The formattedTimeSinceLastSave getter contains hardcoded English text ("1 second ago", "seconds ago", etc.) that should be internationalized. This getter is used in the SaveIndicator tooltip, making it user-facing content.

As per coding guidelines: "Avoid hardcoded user-facing text; use next-intl messages/hooks instead"

Consider one of these approaches:

  1. Move formatting to the UI component where next-intl is available:
// In SaveStateManager, just expose the raw value
get timeSinceLastSave(): number {
    return Math.floor((Date.now() - this.lastSaveTime) / 1000);
}

Then format in the SaveIndicator component using next-intl's date/time formatting utilities or custom translation keys.

  1. Pass a formatter function to the SaveStateManager constructor if formatting must happen in the store layer.

Based on coding guidelines.

🤖 Prompt for AI Agents
In apps/web/client/src/components/store/editor/save-state/index.ts around lines
69 to 76, the getter currently returns hardcoded English strings for elapsed
time which must be internationalized; change the getter to return a raw numeric
value (seconds or minutes) or accept a formatter function injected via the store
constructor, then move string formatting into the UI layer (SaveIndicator) where
next-intl is available (or call the provided formatter) so messages use
next-intl translation utilities instead of hardcoded text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant