-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat: add dedicated Code mode for improved code editing experience #2842
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Introduces a new Code mode that provides a cleaner separation between design and code editing: - Adds Code mode between Design and Preview in the top bar toggle - Creates dedicated CodePanel component on the left side for code editing - Maintains chat panel on the right side in Code mode - Disables design-specific hotkeys (CMD+D, CMD+X, etc.) to avoid conflicts with code editor - Adds "Open in Code" option to right-click context menu for quick access - Hides editor toolbar in Code mode for cleaner interface This change improves the developer experience by preventing conflicts between design and code editing capabilities. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
This pull request has been ignored for the connected project Preview Branches by Supabase. |
WalkthroughAdds a new EditorMode.CODE and integrates a dedicated CodePanel. UI and hotkey behavior are conditioned on mode: entering CODE mode shows the CodePanel, forces CHAT in the right panel, hides certain tabs/toolbars, and disables design hotkeys. A right-click menu item “Open in Code” switches to CODE mode and opens source. Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant RCM as RightClickMenu
participant EE as EditorEngine (state)
participant Main as Main Layout
participant RP as RightPanel
participant CP as CodePanel
participant HK as HotkeysArea
U->>RCM: Right-click • Select "Open in Code"
RCM->>EE: state.editorMode = CODE
RCM->>EE: viewSource(root)
note over EE: editorMode changed to CODE
EE-->>Main: mode change observed
Main->>Main: Recompute layout by mode
alt MODE = CODE
Main-->>CP: Render CodePanel (left)
Main-->>RP: Force selectedTab = CHAT<br/>Hide DEV tab trigger
Main-->>HK: Disable design hotkeys
else MODE = DESIGN
Main-->>CP: Unmount CodePanel
Main-->>RP: Use previous rightPanelTab
Main-->>HK: Enable design hotkeys
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. 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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx (1)
33-55
: Design-mode toggles still fire in Code mode (likely unintended).Keys like
v
(Select),h
(Pan),t
(Insert Text),r
(Insert Div),p
(Preview), space/alt handlers will preempt typing in the code editor. Gate these when in Code mode.-useHotkeys(Hotkey.SELECT.command, () => (editorEngine.state.editorMode = EditorMode.DESIGN)); +useHotkeys(Hotkey.SELECT.command, () => !isCodeMode && (editorEngine.state.editorMode = EditorMode.DESIGN), { enabled: !isCodeMode }); useHotkeys(Hotkey.ESCAPE.command, () => { - editorEngine.state.editorMode = EditorMode.DESIGN; + if (isCodeMode) return; + editorEngine.state.editorMode = EditorMode.DESIGN; if (!editorEngine.text.isEditing) { editorEngine.clearUI(); } }); -useHotkeys(Hotkey.PAN.command, () => (editorEngine.state.editorMode = EditorMode.PAN)); -useHotkeys(Hotkey.PREVIEW.command, () => (editorEngine.state.editorMode = EditorMode.PREVIEW)); +useHotkeys(Hotkey.PAN.command, () => !isCodeMode && (editorEngine.state.editorMode = EditorMode.PAN), { enabled: !isCodeMode }); +useHotkeys(Hotkey.PREVIEW.command, () => !isCodeMode && (editorEngine.state.editorMode = EditorMode.PREVIEW), { enabled: !isCodeMode }); useHotkeys( Hotkey.INSERT_DIV.command, - () => (editorEngine.state.editorMode = EditorMode.INSERT_DIV), + () => !isCodeMode && (editorEngine.state.editorMode = EditorMode.INSERT_DIV), + { enabled: !isCodeMode } ); useHotkeys( Hotkey.INSERT_TEXT.command, - () => (editorEngine.state.editorMode = EditorMode.INSERT_TEXT), + () => !isCodeMode && (editorEngine.state.editorMode = EditorMode.INSERT_TEXT), + { enabled: !isCodeMode } ); -useHotkeys('space', () => (editorEngine.state.editorMode = EditorMode.PAN), { keydown: true }); -useHotkeys('space', () => (editorEngine.state.editorMode = EditorMode.DESIGN), { keyup: true }); -useHotkeys('alt', () => editorEngine.overlay.showMeasurement(), { keydown: true }); -useHotkeys('alt', () => editorEngine.overlay.removeMeasurement(), { keyup: true }); +useHotkeys('space', () => !isCodeMode && (editorEngine.state.editorMode = EditorMode.PAN), { keydown: true, enabled: !isCodeMode }); +useHotkeys('space', () => !isCodeMode && (editorEngine.state.editorMode = EditorMode.DESIGN), { keyup: true, enabled: !isCodeMode }); +useHotkeys('alt', () => !isCodeMode && editorEngine.overlay.showMeasurement(), { keydown: true, enabled: !isCodeMode }); +useHotkeys('alt', () => !isCodeMode && editorEngine.overlay.removeMeasurement(), { keyup: true, enabled: !isCodeMode });
🧹 Nitpick comments (11)
apps/web/client/src/app/project/[id]/_components/right-panel/index.tsx (3)
31-35
: Forcing CHAT in Code mode is correct; consider syncing state to avoid stale tab.You compute
selectedTab
to CHAT in Code mode but leavestate.rightPanelTab
unchanged. Consider setting it to CHAT on mode enter to avoid surprising UI when returning to Design mode.- const selectedTab = editorEngine.state.editorMode === EditorMode.CODE + const selectedTab = editorEngine.state.editorMode === EditorMode.CODE ? EditorTabValue.CHAT : editorEngine.state.rightPanelTab;Outside this block (e.g., where mode changes), also set:
if (value === EditorMode.CODE) editorEngine.state.rightPanelTab = EditorTabValue.CHAT;
84-86
: Hide DEV content based on the effective selected tab, not the raw state.Prevents a mismatch where
rightPanelTab
isDEV
butselectedTab
is forced toCHAT
in Code mode.-<TabsContent forceMount className={cn('h-full overflow-y-auto', editorEngine.state.rightPanelTab !== EditorTabValue.DEV && 'hidden')} value={EditorTabValue.DEV}> +<TabsContent + forceMount + className={cn('h-full overflow-y-auto', selectedTab !== EditorTabValue.DEV && 'hidden')} + value={EditorTabValue.DEV} +>
67-75
: Localize the “Code” tab label.Avoid hardcoded UI strings. Use next‑intl; add a message key (e.g.,
editor.panels.edit.tabs.code.name
) with fallback.- <Icons.Code className="mr-1 h-4 w-4" /> - Code + <Icons.Code className="mr-1 h-4 w-4" /> + {t(transKeys.editor.panels.edit.tabs.code.name, { defaultMessage: 'Code' })}apps/web/client/src/app/project/[id]/_components/main.tsx (1)
111-130
: Fix invalid Tailwind z-index token.
z-49
isn’t a default utility; usez-[49]
(arbitrary value) or an existing scale value.- <div - className="absolute top-10 z-49" + <div + className="absolute top-10 z-[49]"apps/web/client/src/app/project/[id]/_components/right-click-menu/index.tsx (2)
4-4
: Unify model import path.Elsewhere you import from
@onlook/models
; mixing@onlook/models/editor
can cause duplicate module instances in bundlers.-import { EditorMode, EditorTabValue } from '@onlook/models/editor'; +import { EditorMode, EditorTabValue } from '@onlook/models';
161-169
: Localize “Open in Code” and optionally sync right panel to Chat.
- Replace hardcoded label with next‑intl.
- Optional: also set
rightPanelTab = CHAT
for consistency when entering Code mode.-{ - label: 'Open in Code', - action: () => { - editorEngine.state.editorMode = EditorMode.CODE; - viewSource(root); - }, - icon: <Icons.Code className="mr-2 h-4 w-4" />, - disabled: !root, -}, +{ + label: t(transKeys.editor.contextMenu.openInCode, { defaultMessage: 'Open in Code' }), + action: () => { + editorEngine.state.editorMode = EditorMode.CODE; + editorEngine.state.rightPanelTab = EditorTabValue.CHAT; + viewSource(root); + }, + icon: <Icons.Code className="mr-2 h-4 w-4" />, + disabled: !root, +},Note: you’ll need
const t = useTranslations();
and an added keyeditor.contextMenu.openInCode
.apps/web/client/src/app/project/[id]/_components/top-bar/mode-toggle.tsx (2)
56-75
: Localize the “Code” toggle label.Avoid hardcoded text; add a translation key (e.g.,
editor.modes.code.name
) with fallback.-{item.mode === EditorMode.CODE ? 'Code' : t(transKeys.editor.modes[item.mode.toLowerCase() as keyof typeof transKeys.editor.modes].name)} +{item.mode === EditorMode.CODE + ? t(transKeys.editor.modes.code.name, { defaultMessage: 'Code' }) + : t(transKeys.editor.modes[item.mode.toLowerCase() as keyof typeof transKeys.editor.modes].name)}
77-90
: Localize the “Code” toggle label (non‑tooltip branch).Mirror the change above in the branch without hotkeys.
-{item.mode === EditorMode.CODE ? 'Code' : t(transKeys.editor.modes[item.mode.toLowerCase() as keyof typeof transKeys.editor.modes].name)} +{item.mode === EditorMode.CODE + ? t(transKeys.editor.modes.code.name, { defaultMessage: 'Code' }) + : t(transKeys.editor.modes[item.mode.toLowerCase() as keyof typeof transKeys.editor.modes].name)}apps/web/client/src/app/project/[id]/_components/code-panel/index.tsx (2)
6-6
: Use path alias for intra-app imports.Prefer
@/*
over deep relative paths for files underapps/web/client/src/*
.-import { CodeTab } from '../right-panel/code-tab'; +import { CodeTab } from '@/app/project/[id]/_components/right-panel/code-tab';
11-13
: Tailwind: replace non-standardtransition-width
.Use
transition-[width]
or rely on the ResizablePanel’s own animation.- 'flex h-full w-full transition-width duration-300 bg-background/95 group/panel border-[0.5px] backdrop-blur-xl shadow rounded-tr-xl', + 'flex h-full w-full transition-[width] duration-300 bg-background/95 group/panel border-[0.5px] backdrop-blur-xl shadow rounded-tr-xl',apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx (1)
112-129
: Consider gating AI chat hotkeys in Code mode as well.If the intent is to avoid focus-stealing while typing code, also gate AI hotkeys or ensure the editor consumes input first.
-useHotkeys( +useHotkeys( Hotkey.ADD_AI_CHAT.command, - () => (editorEngine.state.rightPanelTab = EditorTabValue.CHAT), + () => !isCodeMode && (editorEngine.state.rightPanelTab = EditorTabValue.CHAT), + { enabled: !isCodeMode } ); -useHotkeys(Hotkey.NEW_AI_CHAT.command, () => { - editorEngine.state.rightPanelTab = EditorTabValue.CHAT; - editorEngine.chat.conversation.startNewConversation(); -}); +useHotkeys(Hotkey.NEW_AI_CHAT.command, () => { + if (isCodeMode) return; + editorEngine.state.rightPanelTab = EditorTabValue.CHAT; + editorEngine.chat.conversation.startNewConversation(); +}, { enabled: !isCodeMode }); useHotkeys( Hotkey.CHAT_MODE_TOGGLE.command, - () => { - editorEngine.state.rightPanelTab = EditorTabValue.CHAT; - // Trigger open chat mode menu - window.dispatchEvent(new CustomEvent('open-chat-mode-menu')); - }, - { preventDefault: true }, + () => { + if (isCodeMode) return; + editorEngine.state.rightPanelTab = EditorTabValue.CHAT; + window.dispatchEvent(new CustomEvent('open-chat-mode-menu')); + }, + { preventDefault: !isCodeMode, enabled: !isCodeMode }, );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx
(2 hunks)apps/web/client/src/app/project/[id]/_components/code-panel/index.tsx
(1 hunks)apps/web/client/src/app/project/[id]/_components/main.tsx
(3 hunks)apps/web/client/src/app/project/[id]/_components/right-click-menu/index.tsx
(2 hunks)apps/web/client/src/app/project/[id]/_components/right-panel/index.tsx
(2 hunks)apps/web/client/src/app/project/[id]/_components/top-bar/mode-toggle.tsx
(3 hunks)packages/models/src/editor/index.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Do not use the any type unless necessary
Files:
packages/models/src/editor/index.ts
apps/web/client/src/app/project/[id]/_components/code-panel/index.tsx
apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx
apps/web/client/src/app/project/[id]/_components/right-panel/index.tsx
apps/web/client/src/app/project/[id]/_components/main.tsx
apps/web/client/src/app/project/[id]/_components/top-bar/mode-toggle.tsx
apps/web/client/src/app/project/[id]/_components/right-click-menu/index.tsx
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
Files:
apps/web/client/src/app/project/[id]/_components/code-panel/index.tsx
apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx
apps/web/client/src/app/project/[id]/_components/right-panel/index.tsx
apps/web/client/src/app/project/[id]/_components/main.tsx
apps/web/client/src/app/project/[id]/_components/top-bar/mode-toggle.tsx
apps/web/client/src/app/project/[id]/_components/right-click-menu/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
Files:
apps/web/client/src/app/project/[id]/_components/code-panel/index.tsx
apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx
apps/web/client/src/app/project/[id]/_components/right-panel/index.tsx
apps/web/client/src/app/project/[id]/_components/main.tsx
apps/web/client/src/app/project/[id]/_components/top-bar/mode-toggle.tsx
apps/web/client/src/app/project/[id]/_components/right-click-menu/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
Files:
apps/web/client/src/app/project/[id]/_components/code-panel/index.tsx
apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx
apps/web/client/src/app/project/[id]/_components/right-panel/index.tsx
apps/web/client/src/app/project/[id]/_components/main.tsx
apps/web/client/src/app/project/[id]/_components/top-bar/mode-toggle.tsx
apps/web/client/src/app/project/[id]/_components/right-click-menu/index.tsx
🧠 Learnings (1)
📚 Learning: 2025-09-14T01:44:21.199Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.199Z
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/project/[id]/_components/code-panel/index.tsx
🧬 Code graph analysis (5)
apps/web/client/src/app/project/[id]/_components/code-panel/index.tsx (1)
packages/ui/src/components/resizable.tsx (1)
ResizablePanel
(88-127)
apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx (1)
apps/web/client/src/components/hotkey.ts (1)
Hotkey
(3-85)
apps/web/client/src/app/project/[id]/_components/right-panel/index.tsx (1)
packages/ui/src/components/icons/index.tsx (1)
Icons
(138-3594)
apps/web/client/src/app/project/[id]/_components/main.tsx (5)
apps/web/client/src/components/store/editor/index.tsx (1)
useEditorEngine
(10-14)apps/web/client/src/app/project/[id]/_hooks/use-panel-measure.tsx (1)
usePanelMeasurements
(5-122)apps/web/client/src/app/project/[id]/_components/left-panel/index.tsx (1)
LeftPanel
(50-154)apps/web/client/src/app/project/[id]/_components/code-panel/index.tsx (1)
CodePanel
(8-27)apps/web/client/src/app/project/[id]/_components/editor-bar/index.tsx (1)
EditorBar
(72-115)
apps/web/client/src/app/project/[id]/_components/top-bar/mode-toggle.tsx (2)
apps/web/client/src/components/hotkey.ts (1)
Hotkey
(3-85)apps/web/client/src/i18n/keys.ts (1)
transKeys
(5-5)
🔇 Additional comments (3)
apps/web/client/src/app/project/[id]/_components/main.tsx (1)
31-34
: Correct: measuring against Code panel in Code mode.Good use of
usePanelMeasurements
withcodePanelRef
vsleftPanelRef
.apps/web/client/src/app/project/[id]/_components/canvas/hotkeys/index.tsx (1)
56-68
: Good gating of undo/redo/enter in Code mode.This prevents editor conflicts in Code mode.
packages/models/src/editor/index.ts (1)
9-10
: Action required: verify downstream handling of new EditorMode.CODE (automation failed)Automated ripgrep run failed ('unrecognized file type: tsx' and PCRE2 compile error); I could not confirm that reducers, persistence, telemetry, and UI mode switches account for EditorMode.CODE. Run these locally and paste results:
rg -n -C3 'editorMode' -g '/*.ts' -g '/.tsx'
rg -n -C3 'EditorMode.' -g '**/.ts' -g '/*.tsx'
rg -n -C2 'EditorMode.(DESIGN|PREVIEW|PAN|INSERT_TEXT|INSERT_DIV|INSERT_IMAGE)' -g '/.ts' -g '**/.tsx'
Introduces a new Code mode that provides a cleaner separation between design and code editing:
This change improves the developer experience by preventing conflicts between design and code editing capabilities.
🤖 Generated with Claude Code
Description
Related Issues
Type of Change
Testing
Screenshots (if applicable)
Additional Notes
Important
Introduces a new Code mode for improved code editing, adding a dedicated code panel and disabling design hotkeys.
Code
mode toEditorMode
enum inindex.ts
.CodePanel
component incode-panel/index.tsx
for code editing.Main
component inmain.tsx
to conditionally renderCodePanel
orLeftPanel
based on mode.right-click-menu/index.tsx
.hotkeys/index.tsx
when in Code mode.Code
mode toggle inmode-toggle.tsx
.main.tsx
.right-panel/index.tsx
.MODE_TOGGLE_ITEMS
inmode-toggle.tsx
to includeCode
mode.RightPanel
to prevent switching to non-chat tabs in Code mode.This description was created by
for a509955. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit