From 8a8c313ee8a0caf2a0abb492d0017ba34532cd68 Mon Sep 17 00:00:00 2001 From: Saravana Achu Mac Date: Mon, 4 May 2026 18:02:46 -0700 Subject: [PATCH] fix(E1): lazy-load code strategy editor --- .../strategy/CodeStrategyEditor.dom.test.tsx | 6 +- .../strategy/CodeStrategyEditor.tsx | 74 ++++++++++++------- web/src/views/ResearchView.tsx | 42 +++++++++-- 3 files changed, 87 insertions(+), 35 deletions(-) diff --git a/web/src/components/strategy/CodeStrategyEditor.dom.test.tsx b/web/src/components/strategy/CodeStrategyEditor.dom.test.tsx index 0057e17..e171ef3 100644 --- a/web/src/components/strategy/CodeStrategyEditor.dom.test.tsx +++ b/web/src/components/strategy/CodeStrategyEditor.dom.test.tsx @@ -49,7 +49,7 @@ describe('CodeStrategyEditor save behavior', () => { const user = userEvent.setup(); render(); - fireEvent.change(screen.getByLabelText('strategy code'), { + fireEvent.change(await screen.findByLabelText('strategy code'), { target: { value: 'function strategy() { return { signal: "HOLD" }; }' }, }); await user.click(screen.getByRole('button', { name: /^save$/i })); @@ -99,7 +99,7 @@ describe('CodeStrategyEditor save behavior', () => { it('saves with Cmd/Ctrl-S while focused in the editor', async () => { render(); - fireEvent.keyDown(screen.getByLabelText('strategy code'), { key: 's', metaKey: true }); + fireEvent.keyDown(await screen.findByLabelText('strategy code'), { key: 's', metaKey: true }); await waitFor(() => expect(createTradeProfileMock).toHaveBeenCalledTimes(1)); }); @@ -112,7 +112,7 @@ describe('CodeStrategyEditor save behavior', () => { vi.stubGlobal('fetch', fetchMock); render(); - fireEvent.keyDown(screen.getByLabelText('strategy code'), { key: 'Enter', metaKey: true }); + fireEvent.keyDown(await screen.findByLabelText('strategy code'), { key: 'Enter', metaKey: true }); await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1)); expect(String(fetchMock.mock.calls[0][0])).toBe('https://trading.test/api/backtest/run'); diff --git a/web/src/components/strategy/CodeStrategyEditor.tsx b/web/src/components/strategy/CodeStrategyEditor.tsx index d898ed6..1909a43 100644 --- a/web/src/components/strategy/CodeStrategyEditor.tsx +++ b/web/src/components/strategy/CodeStrategyEditor.tsx @@ -2,8 +2,7 @@ * Monaco-based code strategy editor. * Users write a JS strategy function; "Run Backtest" posts it to /api/backtest. */ -import { useCallback, useEffect, useRef, useState } from 'react'; -import Editor from '@monaco-editor/react'; +import { Suspense, lazy, useCallback, useEffect, useRef, useState } from 'react'; import { Play, Save, Copy, RotateCcw } from 'lucide-react'; import { getPlatformAccessToken } from '../../lib/authSession'; import { createTradeProfile } from '../../lib/profileApi'; @@ -40,6 +39,8 @@ function strategy({ symbol, price, rsi, ema50, ema200, macd, volume }) { } `; +const MonacoEditor = lazy(() => import('@monaco-editor/react')); + interface BacktestResult { trades?: number; winRate?: number; @@ -231,29 +232,31 @@ export function CodeStrategyEditor({ {/* Monaco editor */}
- { - setCode(v ?? ''); - setSaved(false); - }} - theme="light" - options={{ - fontSize: 13, - minimap: { enabled: false }, - scrollBeyondLastLine: false, - wordWrap: 'on', - lineNumbers: 'on', - renderLineHighlight: 'gutter', - padding: { top: 12, bottom: 12 }, - fontFamily: '"Fira Code", "Cascadia Code", "Consolas", monospace', - fontLigatures: true, - tabSize: 2, - automaticLayout: true, - }} - /> + }> + { + setCode(v ?? ''); + setSaved(false); + }} + theme="light" + options={{ + fontSize: 13, + minimap: { enabled: false }, + scrollBeyondLastLine: false, + wordWrap: 'on', + lineNumbers: 'on', + renderLineHighlight: 'gutter', + padding: { top: 12, bottom: 12 }, + fontFamily: '"Fira Code", "Cascadia Code", "Consolas", monospace', + fontLigatures: true, + tabSize: 2, + automaticLayout: true, + }} + /> +
{/* Error */} @@ -332,6 +335,27 @@ export function CodeStrategyEditor({ ); } +function CodeEditorFallback() { + return ( +
+ Loading code editor… +
+ ); +} + function toolBtn(bg: string, color: string, border: string): React.CSSProperties { return { display: 'flex', alignItems: 'center', gap: 5, diff --git a/web/src/views/ResearchView.tsx b/web/src/views/ResearchView.tsx index 52ed22e..4bb7299 100644 --- a/web/src/views/ResearchView.tsx +++ b/web/src/views/ResearchView.tsx @@ -1,15 +1,18 @@ -import { useState } from 'react'; +import { Suspense, lazy, useState } from 'react'; import { useAppContext } from '../context/AppContext'; import { SignalsTab } from '../tabs/SignalsTab'; import { BacktestTab } from '../tabs/BacktestTab'; import { MyStrategiesTab } from '../tabs/MyStrategiesTab'; import { VisualRuleBuilder, type VisualRule } from '../components/strategy/VisualRuleBuilder'; -import { CodeStrategyEditor } from '../components/strategy/CodeStrategyEditor'; import { createTradeProfile } from '../lib/profileApi'; import { BacktestRunnerPanel } from '../backtest/components/BacktestRunnerPanel'; type ResearchTab = 'Strategies' | 'Visual Builder' | 'Code Editor' | 'Signals' | 'Backtesting'; +const CodeStrategyEditor = lazy(() => + import('../components/strategy/CodeStrategyEditor').then(module => ({ default: module.CodeStrategyEditor })), +); + function buildVisualStrategyConfig(rules: VisualRule[]) { return { type: 'visual', @@ -148,11 +151,13 @@ export function ResearchView() { Write a custom strategy function in JavaScript. Click "Run Backtest" to test it against historical data. - + }> + + )} @@ -166,3 +171,26 @@ export function ResearchView() { ); } + +function CodeStrategyEditorFallback() { + return ( +
+ Loading code strategy editor… +
+ ); +}