Skip to content

Conversation

@paritoshk
Copy link

@paritoshk paritoshk commented Dec 15, 2025

📚 Overview

This PR adds comprehensive architecture documentation for the Onlook codebase.

🎯 Changes

  • ARCHITECTURE.md: New file with detailed documentation including:
    • 8 Mermaid diagrams covering the entire monorepo structure
    • Documentation of all 3 apps (web, backend, admin)
    • Documentation of all 20+ packages with dependencies
    • System architecture and data flow diagrams
    • Runtime architecture and deployment diagrams
    • Development commands cheat sheet

🔍 Why This Matters

This documentation will help:

  • New contributors understand the codebase structure
  • Team members navigate the monorepo
  • Future architectural decisions
  • Onboarding process

📸 Preview

The documentation includes interactive Mermaid diagrams that show:

  • Package dependency graph
  • AI system architecture
  • Parser system flow
  • Database schema relationships
  • User interaction data flows

✅ Checklist

  • Added ARCHITECTURE.md to root of repo
  • Verified all Mermaid syntax is valid
  • Documented all packages and apps
  • Included development workflow information
  • Added .claude/ to .gitignore

Important

Adds comprehensive architecture documentation and introduces save state management with UI indicators in the code editor.

  • Documentation:
    • Adds ARCHITECTURE.md with detailed architecture documentation, including 8 Mermaid diagrams.
    • Covers monorepo structure, system architecture, data flow, runtime architecture, and deployment.
    • Includes documentation for all apps and packages, and a development commands cheat sheet.
  • Save State Management:
    • Introduces SaveStateManager in save-state/index.ts to track save status (saved, saving, unsaved).
    • Updates CodeManager in code/index.ts to use SaveStateManager for save operations.
    • Adds SaveIndicator component in top-bar/save-indicator.tsx to display save status in the UI.
  • Chat Enhancements:
    • Adds ExamplePrompts component in chat-messages/example-prompts.tsx for chat suggestions.
    • Updates ChatMessages in chat-messages/index.tsx to include example prompts when no messages are present.

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

Summary by CodeRabbit

Release Notes

  • New Features

    • Added example prompts to guide AI chat interactions
    • Added save state indicator to display current save status (Saving/Saved/Unsaved)
  • Improvements

    • Latest tool results now automatically expand in chat messages
  • Documentation

    • Added comprehensive architecture documentation

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

paritoshk and others added 2 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 <noreply@anthropic.com>
@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 adds comprehensive architecture documentation, introduces example prompts in the chat interface, implements save state tracking with UI indicators, and refines tool call display with auto-expand behavior for the latest tool calls. Changes span documentation, UI components, and state management.

Changes

Cohort / File(s) Summary
Documentation
.gitignore, ARCHITECTURE.md
Adds .claude/ directory to gitignore; introduces new comprehensive architecture documentation covering monorepo structure, data flows, runtime, deployment, authentication, and system capabilities.
Chat Example Prompts
example-prompts.tsx, apps/web/client/.../chat-tab/chat-messages/index.tsx
New ExamplePrompts component renders categorized prompts by ChatType; integrates into ChatMessages with onSendMessage handler to trigger message sending from prompt selection.
Tool Call Display Refinements
apps/web/client/.../message-content/index.tsx, tool-call-display.tsx, collapsible-code-block.tsx
Adds tracking of last tool call index; ToolCallDisplay receives new isLatest prop to auto-expand latest tool via defaultOpen; CollapsibleCodeBlock adds defaultOpen prop to control initial expansion state.
Chat Integration
apps/web/client/.../chat-tab/chat-tab-content/index.tsx
Passes sendMessage handler to ChatMessages component as onSendMessage prop.
Save Indicator UI
apps/web/client/.../top-bar/index.tsx, save-indicator.tsx
Adds SaveIndicator component to top bar displaying save state (saving/saved/unsaved) with tooltips and timestamps; consumes editor save state via MobX store.
Save State Management
apps/web/client/.../store/editor/code/index.ts, engine.ts, save-state/index.ts
New SaveStateManager class tracks editor save state with lifecycle methods (startSaving, completeSave, markUnsaved) and debounced completion; integrates into EditorEngine and CodeManager.write flow.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Multiple file cohorts spanning UI, state management, and documentation with heterogeneous purposes requiring separate reasoning
  • New SaveStateManager state system interactions with CodeManager and EditorEngine need careful validation
  • Chat UI component prop propagation chain (onSendMessage → ExamplePrompts → message sending) and tool call auto-expand logic should be traced end-to-end
  • SaveIndicator timing logic and debounce mechanisms in save-state/index.ts warrant review for correctness

Possibly related PRs

Poem

🐰 With prompts and saves, the editor sings,
Example whispers guide all things,
States now tracked, from unsaved to done,
The tool calls glow when latest's spun,
A rabbit's blessing: work flows true!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description is comprehensive and well-structured, covering overview, changes, rationale, and a checklist. However, it deviates from the template structure by using emojis and custom formatting instead of the specified template sections (Description, Related Issues, Type of Change, Testing, Screenshots, Additional Notes). Consider restructuring the description to follow the repository template more closely while preserving the detailed content, or clarify if the custom format is acceptable for documentation-focused PRs.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'docs: add comprehensive codebase architecture documentation' accurately summarizes the primary change: adding ARCHITECTURE.md documentation. It is concise, clear, and directly related to the main objective of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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: 4

🧹 Nitpick comments (6)
ARCHITECTURE.md (2)

840-856: Verify and keep concrete version numbers in sync with the codebase

You’re calling out specific versions (Next.js 16, React 19, TailwindCSS v4, TypeScript 5.5+, Bun, etc.). These can drift quickly and become a source of confusion if package.json (or Bun workspaces) aren’t upgraded in lockstep.

I’d either:

  • Confirm these against the current dependencies and keep this section part of your release checklist, or
  • Loosen the wording (e.g. “Next.js (App Router)”, “React”, “TailwindCSS”) and move exact versions to a single source-of-truth (like package.json/bunfig.toml) referenced from here.

865-867: Avoid hedging language around Tailwind config location

The note Tailwind config (likely in @onlook/ui) is ambiguous and will age poorly.

Recommend making this definitive (and adjusting the path if needed), for example:

- /apps/web/client/tailwind.config.ts - Tailwind config (likely in @onlook/ui)
+ /apps/web/client/tailwind.config.ts - Tailwind config
+ /packages/ui/tailwind.config.ts    - Shared Tailwind configuration (if applicable)

(or whatever the actual structure is). This keeps the doc authoritative instead of speculative.

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

80-83: Hardcoded user-facing text should use next-intl.

The "Thinking ..." text should be internationalized using the useTranslations hook that's already imported (Line 38).

Based on learnings, avoid hardcoded user-facing text; use next-intl messages/hooks instead.

 {isStreaming && <div className="flex w-full h-full flex-row items-center gap-2 px-4 my-2 text-small content-start text-foreground-secondary">
     <Icons.LoadingSpinner className="animate-spin" />
-    <p>Thinking ...</p>
+    <p>{t(transKeys.chat.thinking)}</p>
 </div>}

You'll need to add the corresponding key to your translation files (e.g., apps/web/client/messages/*.json).


22-28: Consider using the already-imported ChatType for cleaner typing.

ChatType is available from @onlook/models/chat (Line 6), so the inline import syntax on Line 25 is redundant.

+import { ChatType, type ChatMessage } from '@onlook/models/chat';
-import { type ChatMessage } from '@onlook/models/chat';
 ...
 interface ChatMessagesProps {
     messages: ChatMessage[];
     onEditMessage: EditMessage;
-    onSendMessage: (content: string, type: import('@onlook/models').ChatType) => void;
+    onSendMessage: (content: string, type: ChatType) => void;
     isStreaming: boolean;
     error?: Error;
 }
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx (1)

120-132: Add a fallback for dynamic icon lookup.

If example.icon doesn't match a key in Icons, IconComponent will be undefined and cause a runtime error.

 {prompts.map((example, index) => {
-    const IconComponent = Icons[example.icon];
+    const IconComponent = Icons[example.icon] ?? Icons.QuestionMarkCircled;
     return (
         <Button
             key={index}

Also consider using example.prompt or a unique identifier as the key instead of index if prompts could be reordered dynamically.

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

69-76: Consider moving time formatting to the UI layer for proper i18n.

The formattedTimeSinceLastSave getter returns hardcoded English strings that appear in the user-facing tooltip. For proper internationalization, time formatting should be handled in the UI layer using next-intl's formatting utilities.

Refactor to expose only the numeric value and format in the UI:

-    /**
-     * 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`;
-    }

Then in save-indicator.tsx, use next-intl's useFormatter or format the time with translation keys:

const formatter = useFormatter();
const time = editorEngine.saveState.timeSinceLastSave;
const formattedTime = time < 60 
  ? t('editor.saveIndicator.secondsAgo', { count: time })
  : t('editor.saveIndicator.minutesAgo', { count: Math.floor(time / 60) });

This approach properly separates concerns and enables localization.

📜 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 a991f7b.

📒 Files selected for processing (13)
  • .gitignore (1 hunks)
  • ARCHITECTURE.md (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 (6)
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/index.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/top-bar/save-indicator.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/chat-messages/message-content/tool-call-display.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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.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/index.tsx
  • apps/web/client/src/components/store/editor/code/index.ts
  • 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/top-bar/save-indicator.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/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/message-content/tool-call-display.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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx
  • apps/web/client/src/components/store/editor/save-state/index.ts
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/index.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/top-bar/save-indicator.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/chat-messages/message-content/tool-call-display.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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.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/index.tsx
  • apps/web/client/src/components/store/editor/code/index.ts
  • 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/top-bar/save-indicator.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/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/message-content/tool-call-display.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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx
  • apps/web/client/src/components/store/editor/save-state/index.ts
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/index.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/top-bar/save-indicator.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/chat-messages/message-content/tool-call-display.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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.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/index.tsx
  • apps/web/client/src/components/store/editor/code/index.ts
  • 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/top-bar/save-indicator.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/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/message-content/tool-call-display.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/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/code-display/collapsible-code-block.tsx
  • apps/web/client/src/components/store/editor/save-state/index.ts
🧠 Learnings (9)
📚 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/code/index.ts
  • 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/**/*.{ts,tsx} : Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Applied to files:

  • 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-messages/index.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/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/app/**/*.tsx : Avoid hardcoded user-facing text; use next-intl messages/hooks

Applied to files:

  • 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-messages/index.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/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 : 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/**/*.tsx : Create MobX store instances with useState(() => new Store()) for stable identities across renders

Applied to files:

  • apps/web/client/src/components/store/editor/engine.ts
  • 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:

  • 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/messages/**/*.{json,ts} : Add or modify translation keys in apps/web/client/messages/* and keep keys stable (prefer additions over breaking renames)

Applied to files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/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/**/*.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
📚 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
🧬 Code graph analysis (9)
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)
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/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/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/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-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/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/components/store/editor/save-state/index.ts (1)
apps/web/client/src/components/store/editor/engine.ts (1)
  • EditorEngine (34-138)
🪛 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 (10)
.gitignore (1)

48-50: Tool-specific directory ignore looks good

Ignoring .claude/ alongside other temporary directories is appropriate and keeps tool artifacts out of version control without impacting build/runtime behavior.

ARCHITECTURE.md (1)

1-427: Excellent high-level architecture overview

This is a very strong, cohesive architecture doc: clear layering, consistent package naming, and the data-flow diagrams make the system understandable for both newcomers and maintainers. Nothing blocking here.

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

31-31: LGTM!

The onSendMessage prop is correctly threaded to ChatMessages, enabling the example prompts feature to trigger message sending. The type signature aligns with the sendMessage function from the useChat hook.

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

18-29: LGTM!

The defaultOpen prop implementation is clean:

  • Default value of false maintains backward compatibility
  • useState(defaultOpen) correctly initializes the state once on mount

Note: This is intentionally a one-time initialization—if the parent changes defaultOpen after mount, the collapsible won't re-sync. This is appropriate for the "auto-expand latest tool call" use case.

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

16-23: LGTM!

The isLatest prop is correctly added with a sensible default of false and consistently propagated to all CollapsibleCodeBlock instances for file-editing tools (WriteFileTool, FuzzyEditFileTool, SearchReplaceEditTool, SearchReplaceMultiEditFileTool).

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

35-42: LGTM!

The reverse scan correctly identifies the last tool call for auto-expand behavior.

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

26-49: LGTM! Save lifecycle properly integrated.

The save-state tracking is correctly implemented:

  • startSaving() is called at the beginning of the write operation
  • debouncedCompleteSave() allows batching multiple rapid writes before marking as saved
  • markUnsaved() in the error handler ensures unsaved state is reflected when writes fail
apps/web/client/src/components/store/editor/engine.ts (1)

32-32: LGTM! SaveStateManager properly integrated.

The SaveStateManager follows the same instantiation pattern as other engine managers and is correctly included in the engine's cleanup lifecycle.

Also applies to: 75-75, 119-119

apps/web/client/src/app/project/[id]/_components/top-bar/index.tsx (1)

22-22: LGTM! SaveIndicator appropriately placed in the top bar.

The component is rendered alongside other status indicators and user elements, which is a logical placement for save state visibility.

Also applies to: 54-54

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

6-84: LGTM! Save-state lifecycle correctly implemented.

The SaveStateManager properly handles:

  • State transitions (saved → saving → saved/unsaved)
  • Debounced save completion to batch rapid writes
  • Conditional state updates (markUnsaved respects 'saving' state)
  • Cleanup of pending timeouts

The MobX integration with makeAutoObservable is correct, and the class follows the established manager patterns.

Comment on lines +15 to +84
const EXAMPLE_PROMPTS: Record<ChatType, ExamplePrompt[]> = {
[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',
},
],
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

All user-facing text should be internationalized.

The EXAMPLE_PROMPTS data structure contains numerous hardcoded strings (text and prompt fields) that violate the coding guidelines requiring next-intl for user-facing text. This is a significant amount of content that should be extracted to translation files.

Based on learnings, avoid hardcoded user-facing text; use next-intl messages/hooks instead.

Consider restructuring to use translation keys:

// Define keys structure
const EXAMPLE_PROMPT_KEYS: Record<ChatType, { icon: keyof typeof Icons; textKey: string; promptKey: string }[]> = {
    [ChatType.CREATE]: [
        { icon: 'MagicWand', textKey: 'chat.prompts.create.hero.text', promptKey: 'chat.prompts.create.hero.prompt' },
        // ...
    ],
    // ...
};

// In component, use translations
const t = useTranslations();
const prompts = EXAMPLE_PROMPT_KEYS[chatMode].map(item => ({
    icon: item.icon,
    text: t(item.textKey),
    prompt: t(item.promptKey),
}));
🤖 Prompt for AI Agents
In
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
around lines 15–84, the EXAMPLE_PROMPTS object contains hardcoded user-facing
strings (text and prompt) that must be internationalized; replace this structure
with a keys-only configuration (keep icon values) that references translation
keys (e.g. chat.prompts.<chatType>.<item>.(text|prompt)), move all literal
strings into the locale JSON/TS translation files, and in the consuming
component use next-intl's useTranslations to map keys to localized strings at
render time (build the final prompts array by calling t(key) for text and
prompt); update related types to accept keys, ensure icons remain the same, and
run the app to verify no missing translation keys.

Comment on lines +95 to +108
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';
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Internationalize getModeTitle() return values.

The hardcoded strings in getModeTitle() should use translations.

Based on learnings, avoid hardcoded user-facing text; use next-intl messages/hooks instead.

+import { useTranslations } from 'next-intl';
+import { transKeys } from '@/i18n/keys';

 export const ExamplePrompts = observer(({ onSelectPrompt }: ExamplePromptsProps) => {
     const editorEngine = useEditorEngine();
+    const t = useTranslations();
     const chatMode = editorEngine.state.chatMode;
     // ...
     
     const getModeTitle = () => {
         switch (chatMode) {
             case ChatType.CREATE:
-                return 'Create something new';
+                return t(transKeys.chat.prompts.titles.create);
             // ... similar for other cases
         }
     };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
around lines 95 to 108, replace the hardcoded return strings in getModeTitle()
with calls to the next-intl translation hook (e.g., useTranslations) or the
messages object (t('chat.mode.create') etc. — import and initialize the
translator at the top of the component, map each ChatType case to a message key
(create, edit, ask, fix, default) and return t('path.to.key') instead of literal
text, and add the corresponding keys and translated values to the locale JSON
files so there is a fallback for all supported locales.

Comment on lines +116 to +118
<p className="text-sm text-foreground-tertiary/80 mb-6 text-center max-w-[300px]">
Start a conversation or try one of these examples
</p>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Internationalize the description text.

The paragraph text should use translations.

Based on learnings, avoid hardcoded user-facing text; use next-intl messages/hooks instead.

 <p className="text-sm text-foreground-tertiary/80 mb-6 text-center max-w-[300px]">
-    Start a conversation or try one of these examples
+    {t(transKeys.chat.prompts.description)}
 </p>

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/example-prompts.tsx
around lines 116-118, the paragraph contains hardcoded user-facing text; replace
it with a translated message using next-intl (e.g., import and call
useTranslations or useMessage hook at the top of the component, add a messages
key like "chat.examplePrompt" to the locale files, then render
t('chat.examplePrompt') instead of the hardcoded string). Ensure you add the new
key to the appropriate locale JSON(s) and keep the original styling/props
unchanged.

Comment on lines +13 to +48
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>
</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>
</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>
</div>
);
}
};

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';
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Internationalize user-facing text with next-intl.

Multiple hardcoded English strings violate the coding guidelines for this path. Use next-intl messages instead.

As per coding guidelines, files matching apps/web/client/src/app/**/*.tsx should avoid hardcoded user-facing text and use next-intl messages/hooks.

Apply this pattern to refactor the component:

+'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';
+import { useTranslations } from 'next-intl';

 export const SaveIndicator = observer(() => {
     const editorEngine = useEditorEngine();
     const saveState: SaveState = editorEngine.saveState.saveState;
+    const t = useTranslations();

     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('editor.saveIndicator.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('editor.saveIndicator.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('editor.saveIndicator.unsavedChanges')}</span>
                     </div>
                 );
         }
     };

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

Then add the corresponding keys to your translation files (e.g., messages/en.json):

{
  "editor": {
    "saveIndicator": {
      "saving": "Saving...",
      "saved": "Saved",
      "unsavedChanges": "Unsaved changes",
      "tooltipSaving": "Saving your changes...",
      "tooltipSaved": "Last saved {time}",
      "tooltipUnsaved": "You have unsaved changes"
    }
  }
}
🤖 Prompt for AI Agents
In apps/web/client/src/app/project/[id]/_components/top-bar/save-indicator.tsx
around lines 13 to 48, several user-facing strings are hardcoded; replace them
with next-intl message lookups: import and call useTranslations (e.g., const t =
useTranslations()), replace "Saving...", "Saved", "Unsaved changes", "Saving
your changes...", "Last saved {time}", and "You have unsaved changes" with
t('editor.saveIndicator.saving'), t('editor.saveIndicator.saved'),
t('editor.saveIndicator.unsavedChanges'),
t('editor.saveIndicator.tooltipSaving'), t('editor.saveIndicator.tooltipSaved',
{ time: editorEngine.saveState.formattedTimeSinceLastSave }), and
t('editor.saveIndicator.tooltipUnsaved') respectively, ensuring interpolation
for the saved tooltip; then add the corresponding keys to messages/en.json as
provided and run a build/lint to confirm no remaining hardcoded strings.

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